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