3
* $Id: MemBuf.cc,v 1.42 2006/09/20 08:13:38 adrian Exp $
5
* DEBUG: section 59 auto-growing Memory Buffer with printf
6
* AUTHOR: Alex Rousskov
8
* SQUID Web Proxy Cache http://www.squid-cache.org/
9
* ----------------------------------------------------------
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.
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.
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.
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.
37
* To-Do: use memory pools for .buf recycling @?@ @?@
44
* Here is how one would comm_write an object without MemBuffer:
48
* buf = malloc(big_enough);
51
* snprintf object(s) piece-by-piece constantly checking for overflows
52
* and maintaining (buf+offset);
56
* comm_write(buf, free, ...);
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.
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
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.
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
80
* MemBuffer eliminates both "packing" mess and truncated data:
86
* -- required init with optional size tuning (see #defines for defaults)
87
* buf.init(initial-size, absolute-maximum);
89
* -- "pack" (no need to handle offsets or check for overflows)
94
* comm_write_mbuf(fd, buf, handler, data);
96
* -- *iff* you did not give the buffer away, free it yourself
100
/* if you have configure you can use this */
101
#if defined(HAVE_CONFIG_H)
108
#if defined HAVE_VA_COPY
109
#define VA_COPY va_copy
110
#elif defined HAVE___VA_COPY
111
#define VA_COPY __va_copy
117
/* local constants */
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)
123
CBDATA_CLASS_INIT(MemBuf);
125
/* init with defaults */
129
init(MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE);
133
/* init with specific sizes */
135
MemBuf::init(mb_size_t szInit, mb_size_t szMax)
137
assert(szInit > 0 && szMax > 0);
140
max_capacity = szMax;
147
* cleans the mb; last function to call if you do not give .buf away with
157
assert(!stolen); /* not frozen */
159
memFreeBuf(capacity, buf);
161
size = capacity = max_capacity = 0;
165
/* cleans the buffer without changing its capacity
166
* if called with a Null buffer, calls memBufDefInit() */
173
assert(!stolen); /* not frozen */
175
memset(buf, 0, capacity);
180
/* unfortunate hack to test if the buffer has been Init()ialized */
184
if (!buf && !max_capacity && !capacity && !size)
185
return 1; /* is null (not initialized) */
187
assert(buf && max_capacity && capacity); /* paranoid */
192
mb_size_t MemBuf::spaceSize() const
194
const mb_size_t terminatedSize = size + 1;
195
return (terminatedSize < capacity) ? capacity - terminatedSize : 0;
198
mb_size_t MemBuf::potentialSpaceSize() const
200
const mb_size_t terminatedSize = size + 1;
201
return (terminatedSize < max_capacity) ? max_capacity - terminatedSize : 0;
204
// removes sz bytes and "packs" by moving content left
205
void MemBuf::consume(mb_size_t shiftSize)
207
const mb_size_t cSize = contentSize();
208
assert(0 <= shiftSize && shiftSize <= cSize);
209
assert(!stolen); /* not frozen */
211
PROF_start(MemBuf_consume);
213
if (shiftSize < cSize)
214
xmemmove(buf, buf + shiftSize, cSize - shiftSize);
220
PROF_stop(MemBuf_consume);
223
// calls memcpy, appends exactly size bytes, extends buffer if needed
224
void MemBuf::append(const char *newContent, mb_size_t sz)
228
assert(!stolen); /* not frozen */
230
PROF_start(MemBuf_append);
232
if (size + sz + 1 > capacity)
235
assert(size + sz <= capacity); /* paranoid */
237
xmemcpy(space(), newContent, sz);
241
PROF_stop(MemBuf_append);
244
// updates content size after external append
245
void MemBuf::appended(mb_size_t sz)
247
assert(size + sz <= capacity);
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()
259
assert(size < capacity);
263
/* calls memBufVPrintf */
266
MemBuf::Printf(const char *fmt,...)
272
MemBuf::Printf(va_alist)
278
const char *fmt = va_arg(args, char *);
286
/* vPrintf for other printf()'s to use; calls vsnprintf, extends buf if needed */
288
MemBuf::vPrintf(const char *fmt, va_list vargs) {
296
assert(!stolen); /* not frozen */
297
/* assert in Grow should quit first, but we do not want to have a scary infinite loop */
299
while (capacity <= max_capacity) {
300
mb_size_t free_space = capacity - size;
301
/* put as much as we can */
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.
309
sz = vsnprintf(buf + size, free_space, fmt, ap);
313
sz = vsnprintf(buf + size, free_space, fmt, vargs);
315
/* check for possible overflow */
316
/* snprintf on Linuz returns -1 on overflows */
317
/* snprintf on FreeBSD returns at least free_space on overflows */
319
if (sz < 0 || sz >= free_space)
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 */
330
if (!size || buf[size - 1]) {
338
* returns free() function to be used.
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)
348
assert(!stolen); /* not frozen */
350
ff = memFreeBufFunc((size_t) capacity);
351
stolen = 1; /* freeze */
355
/* grows (doubles) internal buffer to satisfy required minimal capacity */
357
MemBuf::grow(mb_size_t min_cap) {
362
assert(capacity < min_cap);
364
PROF_start(MemBuf_grow);
366
/* determine next capacity */
368
if (min_cap > 64 * 1024) {
371
while (new_cap < (size_t) min_cap)
372
new_cap += 64 * 1024; /* increase in reasonable steps */
374
new_cap = (size_t) min_cap;
377
/* last chance to fit before we assert(!overflow) */
378
if (new_cap > (size_t) max_capacity)
379
new_cap = (size_t) max_capacity;
381
assert(new_cap <= (size_t) max_capacity); /* no overflow */
383
assert(new_cap > (size_t) capacity); /* progress */
385
buf_cap = (size_t) capacity;
387
buf = (char *)memReallocBuf(buf, new_cap, &buf_cap);
390
capacity = (mb_size_t) buf_cap;
391
PROF_stop(MemBuf_grow);
397
/* puts report on MemBuf _module_ usage into mb */
399
memBufReport(MemBuf * mb) {
401
mb->Printf("memBufReport is not yet implemented @?@\n");
405
#include "MemBuf.cci"