a b/src/utils/zlibut.cpp
1
/* Copyright (C) 2017 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
18
#include "zlibut.h"
19
20
#include <zlib.h>
21
22
#include "log.h"
23
24
using namespace std;
25
26
static void *allocmem(
27
    void *cp,    /* The array to grow. may be NULL */
28
    int  sz,     /* Unit size in bytes */
29
    int  *np,    /* Pointer to current allocation number */
30
    int  min,    /* Number to allocate the first time */
31
    int  maxinc) /* Maximum increment */
32
{
33
    if (cp == 0) {
34
        cp = malloc(min * sz);
35
        *np = cp ? min : 0;
36
        return cp;
37
    }
38
39
    int inc = (*np > maxinc) ?  maxinc : *np;
40
    if ((cp = realloc(cp, (*np + inc) * sz)) != 0) {
41
        *np += inc;
42
    }
43
    return cp;
44
}
45
46
class ZLibUtBuf::Internal {
47
public:
48
    Internal() {}
49
    ~Internal() {
50
        if (buf && dofree) {
51
            free(buf);
52
        }
53
    }
54
    bool grow(size_t n) {
55
        if (!initsz)
56
            initsz = n;
57
        buf = (char *)allocmem(buf, initsz, &alloc, 1, 20);
58
        return nullptr != buf;
59
    }
60
    int getAlloc() {
61
        return alloc * initsz;
62
    }
63
    char *buf{nullptr};
64
    int initsz{0}; // Set to first alloc size
65
    int alloc{0}; // Allocation count (allocmem()). Capa is alloc*inisz
66
    int datacnt{0}; // Data count
67
    bool dofree{true}; // Does buffer belong to me ?
68
    friend bool inflateToBuf(void* inp, unsigned int inlen, ZLibUtBuf& buf);
69
};
70
71
ZLibUtBuf::ZLibUtBuf()
72
{
73
    m = new Internal;
74
}
75
ZLibUtBuf::~ZLibUtBuf()
76
{
77
    delete m;
78
}
79
80
char *ZLibUtBuf::getBuf() const
81
{
82
    return m->buf;
83
}            
84
char *ZLibUtBuf::takeBuf()
85
{
86
    m->dofree = false;
87
    return m->buf;
88
}
89
size_t ZLibUtBuf::getCnt()
90
{
91
    return m->datacnt;
92
}
93
94
bool inflateToBuf(const void* inp, unsigned int inlen, ZLibUtBuf& buf)
95
{
96
    LOGDEB0("inflateToBuf: inlen " << inlen << "\n");
97
98
    z_stream d_stream; /* decompression stream */
99
100
    d_stream.zalloc = (alloc_func)0;
101
    d_stream.zfree = (free_func)0;
102
    d_stream.opaque = (voidpf)0;
103
    d_stream.next_in  = (Bytef*)inp;
104
    d_stream.avail_in = inlen;
105
    d_stream.next_out = 0;
106
    d_stream.avail_out = 0;
107
108
    int err;
109
    if ((err = inflateInit(&d_stream)) != Z_OK) {
110
        LOGERR("Inflate: inflateInit: err " << err << " msg "  <<
111
               d_stream.msg << "\n");
112
        return false;
113
    }
114
115
    for (;;) {
116
        LOGDEB2("InflateToDynBuf: avail_in " << d_stream.avail_in <<
117
                " total_in " << d_stream.total_in << " avail_out " <<
118
                d_stream.avail_out << " total_out " << d_stream.total_out <<
119
                "\n");
120
        if (d_stream.avail_out == 0) {
121
            if (!buf.m->grow(inlen)) {
122
                LOGERR("Inflate: out of memory, current alloc " <<
123
                       buf.m->getAlloc() << "\n");
124
                inflateEnd(&d_stream);
125
                return false;
126
            }
127
            d_stream.avail_out = buf.m->getAlloc() - d_stream.total_out;
128
            d_stream.next_out = (Bytef*)(buf.getBuf() + d_stream.total_out);
129
        }
130
        err = inflate(&d_stream, Z_NO_FLUSH);
131
        if (err == Z_STREAM_END) {
132
            break;
133
        }
134
        if (err != Z_OK) {
135
            LOGERR("Inflate: error " << err << " msg " <<
136
                   (d_stream.msg ? d_stream.msg : "") << endl);
137
            inflateEnd(&d_stream);
138
            return false;
139
        }
140
    }
141
    if ((err = inflateEnd(&d_stream)) != Z_OK) {
142
        LOGERR("Inflate: inflateEnd error " << err << " msg " <<
143
               (d_stream.msg ? d_stream.msg : "") << endl);
144
        return false;
145
    }
146
    buf.m->datacnt = d_stream.total_out;
147
    LOGDEB1("inflateToBuf: ok, output size " << buf.getCnt() << endl);
148
    return true;
149
}
150
151
152
bool deflateToBuf(const void* inp, unsigned int inlen, ZLibUtBuf& buf)
153
{
154
    uLongf len = compressBound(static_cast<uLong>(inlen));
155
    // This needs cleanup: because the buffer is reused inside
156
    // e.g. circache, we want a minimum size in case the 1st doc size,
157
    // which sets the grow increment is small. It would be better to
158
    // let the user set a min size hint.
159
    if (len < 500 *1024)
160
        len = 500 * 1024;
161
162
    while (buf.m->getAlloc() < int(len)) {
163
        if (!buf.m->grow(len)) {
164
            LOGERR("deflateToBuf: can't get buffer for " << len << " bytes\n");
165
            return false;
166
        }
167
    }
168
    bool ret = compress((Bytef*)buf.getBuf(), &len, (Bytef*)inp,
169
                        static_cast<uLong>(inlen)) == Z_OK;
170
    buf.m->datacnt = len;
171
    return ret;
172
}