~clint-fewbar/ubuntu/precise/squid3/ignore-sighup-early

« back to all changes in this revision

Viewing changes to src/MemBuf.cc

  • Committer: Bazaar Package Importer
  • Author(s): Luigi Gangitano
  • Date: 2006-11-11 10:32:06 UTC
  • Revision ID: james.westby@ubuntu.com-20061111103206-f3p0r9g0vq44rp3r
Tags: upstream-3.0.PRE5
ImportĀ upstreamĀ versionĀ 3.0.PRE5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*
 
3
 * $Id: MemBuf.cc,v 1.42 2006/09/20 08:13:38 adrian Exp $
 
4
 *
 
5
 * DEBUG: section 59    auto-growing Memory Buffer with printf
 
6
 * AUTHOR: Alex Rousskov
 
7
 *
 
8
 * SQUID Web Proxy Cache          http://www.squid-cache.org/
 
9
 * ----------------------------------------------------------
 
10
 *
 
11
 *  Squid is the result of efforts by numerous individuals from
 
12
 *  the Internet community; see the CONTRIBUTORS file for full
 
13
 *  details.   Many organizations have provided support for Squid's
 
14
 *  development; see the SPONSORS file for full details.  Squid is
 
15
 *  Copyrighted (C) 2001 by the Regents of the University of
 
16
 *  California; see the COPYRIGHT file for full details.  Squid
 
17
 *  incorporates software developed and/or copyrighted by other
 
18
 *  sources; see the CREDITS file for full details.
 
19
 *
 
20
 *  This program is free software; you can redistribute it and/or modify
 
21
 *  it under the terms of the GNU General Public License as published by
 
22
 *  the Free Software Foundation; either version 2 of the License, or
 
23
 *  (at your option) any later version.
 
24
 *  
 
25
 *  This program is distributed in the hope that it will be useful,
 
26
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
27
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
28
 *  GNU General Public License for more details.
 
29
 *  
 
30
 *  You should have received a copy of the GNU General Public License
 
31
 *  along with this program; if not, write to the Free Software
 
32
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 
33
 *
 
34
 */
 
35
 
 
36
/*
 
37
 * To-Do: use memory pools for .buf recycling @?@ @?@
 
38
 */
 
39
 
 
40
/*
 
41
 * Rationale:
 
42
 * ----------
 
43
 * 
 
44
 * Here is how one would comm_write an object without MemBuffer:
 
45
 * 
 
46
 * {
 
47
 * -- allocate:
 
48
 * buf = malloc(big_enough);
 
49
 * 
 
50
 * -- "pack":
 
51
 * snprintf object(s) piece-by-piece constantly checking for overflows
 
52
 * and maintaining (buf+offset);
 
53
 * ...
 
54
 * 
 
55
 * -- write
 
56
 * comm_write(buf, free, ...);
 
57
 * }
 
58
 * 
 
59
 * The whole "packing" idea is quite messy: We are given a buffer of fixed
 
60
 * size and we have to check all the time that we still fit. Sounds logical.
 
61
 *
 
62
 * However, what happens if we have more data? If we are lucky to stop before
 
63
 * we overrun any buffers, we still may have garbage (e.g. half of ETag) in
 
64
 * the buffer.
 
65
 * 
 
66
 * MemBuffer:
 
67
 * ----------
 
68
 * 
 
69
 * MemBuffer is a memory-resident buffer with printf()-like interface. It
 
70
 * hides all offest handling and overflow checking. Moreover, it has a
 
71
 * build-in control that no partial data has been written.
 
72
 * 
 
73
 * MemBuffer is designed to handle relatively small data. It starts with a
 
74
 * small buffer of configurable size to avoid allocating huge buffers all the
 
75
 * time.  MemBuffer doubles the buffer when needed. It assert()s that it will
 
76
 * not grow larger than a configurable limit. MemBuffer has virtually no
 
77
 * overhead (and can even reduce memory consumption) compared to old
 
78
 * "packing" approach.
 
79
 * 
 
80
 * MemBuffer eliminates both "packing" mess and truncated data:
 
81
 * 
 
82
 * {
 
83
 * -- setup
 
84
 * MemBuf buf;
 
85
 * 
 
86
 * -- required init with optional size tuning (see #defines for defaults)
 
87
 * buf.init(initial-size, absolute-maximum);
 
88
 * 
 
89
 * -- "pack" (no need to handle offsets or check for overflows)
 
90
 * buf.Printf(...);
 
91
 * ...
 
92
 * 
 
93
 * -- write
 
94
 * comm_write_mbuf(fd, buf, handler, data);
 
95
 *
 
96
 * -- *iff* you did not give the buffer away, free it yourself
 
97
 * -- buf.clean();
 
98
 * }
 
99
 */
 
100
/* if you have configure you can use this */
 
101
#if defined(HAVE_CONFIG_H)
 
102
#include "config.h"
 
103
#endif
 
104
 
 
105
#ifdef VA_COPY
 
106
#undef VA_COPY
 
107
#endif
 
108
#if defined HAVE_VA_COPY
 
109
#define VA_COPY va_copy
 
110
#elif defined HAVE___VA_COPY
 
111
#define VA_COPY __va_copy
 
112
#endif
 
113
 
 
114
#include "squid.h"
 
115
#include "MemBuf.h"
 
116
 
 
117
/* local constants */
 
118
 
 
119
/* default values for buffer sizes, used by memBufDefInit */
 
120
#define MEM_BUF_INIT_SIZE   (2*1024)
 
121
#define MEM_BUF_MAX_SIZE    (2*1000*1024*1024)
 
122
 
 
123
CBDATA_CLASS_INIT(MemBuf);
 
124
 
 
125
/* init with defaults */
 
126
void
 
127
MemBuf::init()
 
128
{
 
129
    init(MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE);
 
130
}
 
131
 
 
132
 
 
133
/* init with specific sizes */
 
134
void
 
135
MemBuf::init(mb_size_t szInit, mb_size_t szMax)
 
136
{
 
137
    assert(szInit > 0 && szMax > 0);
 
138
    buf = NULL;
 
139
    size = 0;
 
140
    max_capacity = szMax;
 
141
    capacity = 0;
 
142
    stolen = 0;
 
143
    grow(szInit);
 
144
}
 
145
 
 
146
/*
 
147
 * cleans the mb; last function to call if you do not give .buf away with
 
148
 * memBufFreeFunc
 
149
 */
 
150
void
 
151
MemBuf::clean()
 
152
{
 
153
    if (isNull()) {
 
154
        // nothing to do
 
155
    } else {
 
156
        assert(buf);
 
157
        assert(!stolen);        /* not frozen */
 
158
 
 
159
        memFreeBuf(capacity, buf);
 
160
        buf = NULL;
 
161
        size = capacity = max_capacity = 0;
 
162
    }
 
163
}
 
164
 
 
165
/* cleans the buffer without changing its capacity
 
166
 * if called with a Null buffer, calls memBufDefInit() */
 
167
void
 
168
MemBuf::reset()
 
169
{
 
170
    if (isNull()) {
 
171
        init();
 
172
    } else {
 
173
        assert(!stolen);        /* not frozen */
 
174
        /* reset */
 
175
        memset(buf, 0, capacity);
 
176
        size = 0;
 
177
    }
 
178
}
 
179
 
 
180
/* unfortunate hack to test if the buffer has been Init()ialized */
 
181
int
 
182
MemBuf::isNull()
 
183
{
 
184
    if (!buf && !max_capacity && !capacity && !size)
 
185
        return 1;               /* is null (not initialized) */
 
186
 
 
187
    assert(buf && max_capacity && capacity);    /* paranoid */
 
188
 
 
189
    return 0;
 
190
}
 
191
 
 
192
mb_size_t MemBuf::spaceSize() const
 
193
{
 
194
    const mb_size_t terminatedSize = size + 1;
 
195
    return (terminatedSize < capacity) ? capacity - terminatedSize : 0;
 
196
}
 
197
 
 
198
mb_size_t MemBuf::potentialSpaceSize() const
 
199
{
 
200
    const mb_size_t terminatedSize = size + 1;
 
201
    return (terminatedSize < max_capacity) ? max_capacity - terminatedSize : 0;
 
202
}
 
203
 
 
204
// removes sz bytes and "packs" by moving content left
 
205
void MemBuf::consume(mb_size_t shiftSize)
 
206
{
 
207
    const mb_size_t cSize = contentSize();
 
208
    assert(0 <= shiftSize && shiftSize <= cSize);
 
209
    assert(!stolen); /* not frozen */
 
210
 
 
211
    PROF_start(MemBuf_consume);
 
212
    if (shiftSize > 0) {
 
213
        if (shiftSize < cSize)
 
214
            xmemmove(buf, buf + shiftSize, cSize - shiftSize);
 
215
 
 
216
        size -= shiftSize;
 
217
 
 
218
        terminate();
 
219
    }
 
220
    PROF_stop(MemBuf_consume);
 
221
}
 
222
 
 
223
// calls memcpy, appends exactly size bytes, extends buffer if needed
 
224
void MemBuf::append(const char *newContent, mb_size_t sz)
 
225
{
 
226
    assert(sz >= 0);
 
227
    assert(buf);
 
228
    assert(!stolen); /* not frozen */
 
229
 
 
230
    PROF_start(MemBuf_append);
 
231
    if (sz > 0) {
 
232
        if (size + sz + 1 > capacity)
 
233
            grow(size + sz + 1);
 
234
 
 
235
        assert(size + sz <= capacity); /* paranoid */
 
236
 
 
237
        xmemcpy(space(), newContent, sz);
 
238
 
 
239
        appended(sz);
 
240
    }
 
241
    PROF_stop(MemBuf_append);
 
242
}
 
243
 
 
244
// updates content size after external append
 
245
void MemBuf::appended(mb_size_t sz)
 
246
{
 
247
    assert(size + sz <= capacity);
 
248
    size += sz;
 
249
    terminate();
 
250
}
 
251
 
 
252
// 0-terminate in case we are used as a string.
 
253
// Extra octet is not counted in the content size (or space size)
 
254
// XXX: but the extra octet is counted when growth decisions are made!
 
255
// This will cause the buffer to grow when spaceSize() == 1 on append,
 
256
// which will assert() if the buffer cannot grow any more.
 
257
void MemBuf::terminate()
 
258
{
 
259
    assert(size < capacity);
 
260
    *space() = '\0';
 
261
}
 
262
 
 
263
/* calls memBufVPrintf */
 
264
#if STDC_HEADERS
 
265
void
 
266
MemBuf::Printf(const char *fmt,...)
 
267
{
 
268
    va_list args;
 
269
    va_start(args, fmt);
 
270
#else
 
271
void
 
272
MemBuf::Printf(va_alist)
 
273
va_dcl
 
274
{
 
275
    va_list args;
 
276
    mb_size_t sz = 0;
 
277
    va_start(args);
 
278
    const char *fmt = va_arg(args, char *);
 
279
#endif
 
280
 
 
281
    vPrintf(fmt, args);
 
282
    va_end(args);
 
283
}
 
284
 
 
285
 
 
286
/* vPrintf for other printf()'s to use; calls vsnprintf, extends buf if needed */
 
287
void
 
288
MemBuf::vPrintf(const char *fmt, va_list vargs) {
 
289
#ifdef VA_COPY
 
290
    va_list ap;
 
291
#endif
 
292
 
 
293
    int sz = 0;
 
294
    assert(fmt);
 
295
    assert(buf);
 
296
    assert(!stolen);    /* not frozen */
 
297
    /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
 
298
 
 
299
    while (capacity <= max_capacity) {
 
300
        mb_size_t free_space = capacity - size;
 
301
        /* put as much as we can */
 
302
 
 
303
#ifdef VA_COPY
 
304
        /* Fix of bug 753r. The value of vargs is undefined
 
305
         * after vsnprintf() returns. Make a copy of vargs
 
306
         * incase we loop around and call vsnprintf() again.
 
307
         */
 
308
        VA_COPY(ap,vargs);
 
309
        sz = vsnprintf(buf + size, free_space, fmt, ap);
 
310
        va_end(ap);
 
311
#else /* VA_COPY */
 
312
 
 
313
        sz = vsnprintf(buf + size, free_space, fmt, vargs);
 
314
#endif /*VA_COPY*/
 
315
        /* check for possible overflow */
 
316
        /* snprintf on Linuz returns -1 on overflows */
 
317
        /* snprintf on FreeBSD returns at least free_space on overflows */
 
318
 
 
319
        if (sz < 0 || sz >= free_space)
 
320
            grow(capacity + 1);
 
321
        else
 
322
            break;
 
323
    }
 
324
 
 
325
    size += sz;
 
326
    /* on Linux and FreeBSD, '\0' is not counted in return value */
 
327
    /* on XXX it might be counted */
 
328
    /* check that '\0' is appended and not counted */
 
329
 
 
330
    if (!size || buf[size - 1]) {
 
331
        assert(!buf[size]);
 
332
    } else {
 
333
        size--;
 
334
    }
 
335
}
 
336
 
 
337
/*
 
338
 * returns free() function to be used.
 
339
 * Important:
 
340
 *   calling this function "freezes" mb,
 
341
 *   do not _update_ mb after that in any way
 
342
 *   (you still can read-access .buf and .size)
 
343
 */
 
344
FREE *
 
345
MemBuf::freeFunc() {
 
346
    FREE *ff;
 
347
    assert(buf);
 
348
    assert(!stolen);    /* not frozen */
 
349
 
 
350
    ff = memFreeBufFunc((size_t) capacity);
 
351
    stolen = 1;         /* freeze */
 
352
    return ff;
 
353
}
 
354
 
 
355
/* grows (doubles) internal buffer to satisfy required minimal capacity */
 
356
void
 
357
MemBuf::grow(mb_size_t min_cap) {
 
358
    size_t new_cap;
 
359
    size_t buf_cap;
 
360
 
 
361
    assert(!stolen);
 
362
    assert(capacity < min_cap);
 
363
 
 
364
    PROF_start(MemBuf_grow);
 
365
 
 
366
    /* determine next capacity */
 
367
 
 
368
    if (min_cap > 64 * 1024) {
 
369
        new_cap = 64 * 1024;
 
370
 
 
371
        while (new_cap < (size_t) min_cap)
 
372
            new_cap += 64 * 1024;       /* increase in reasonable steps */
 
373
    } else {
 
374
        new_cap = (size_t) min_cap;
 
375
    }
 
376
 
 
377
    /* last chance to fit before we assert(!overflow) */
 
378
    if (new_cap > (size_t) max_capacity)
 
379
        new_cap = (size_t) max_capacity;
 
380
 
 
381
    assert(new_cap <= (size_t) max_capacity);   /* no overflow */
 
382
 
 
383
    assert(new_cap > (size_t) capacity);        /* progress */
 
384
 
 
385
    buf_cap = (size_t) capacity;
 
386
 
 
387
    buf = (char *)memReallocBuf(buf, new_cap, &buf_cap);
 
388
 
 
389
    /* done */
 
390
    capacity = (mb_size_t) buf_cap;
 
391
    PROF_stop(MemBuf_grow);
 
392
}
 
393
 
 
394
 
 
395
/* Reports */
 
396
 
 
397
/* puts report on MemBuf _module_ usage into mb */
 
398
void
 
399
memBufReport(MemBuf * mb) {
 
400
    assert(mb);
 
401
    mb->Printf("memBufReport is not yet implemented @?@\n");
 
402
}
 
403
 
 
404
#ifndef _USE_INLINE_
 
405
#include "MemBuf.cci"
 
406
#endif