]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemBuf.cc
Must call cbdataReferenceDone on the ident_lookup list
[thirdparty/squid.git] / src / MemBuf.cc
CommitLineData
f740a279 1
cb69b4c7 2/*
4eb368f9 3 * $Id: MemBuf.cc,v 1.37 2004/12/21 17:52:53 robertc Exp $
cb69b4c7 4 *
123abbe1 5 * DEBUG: section 59 auto-growing Memory Buffer with printf
cb69b4c7 6 * AUTHOR: Alex Rousskov
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
cb69b4c7 10 *
2b6662ba 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.
cb69b4c7 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
cb69b4c7 34 */
35
cb69b4c7 36/*
2246b732 37 * To-Do: use memory pools for .buf recycling @?@ @?@
cb69b4c7 38 */
39
1a3db59c 40/*
2ac76861 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.
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.
2ac76861 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 * memBufInit(&buf, initial-size, absolute-maximum);
88 *
89 * -- "pack" (no need to handle offsets or check for overflows)
90 * memBufPrintf(&buf, ...);
91 * ...
92 *
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
51251ed1 97 * -- memBufClean(&buf);
2ac76861 98 * }
99 */
eee79a2e 100/* if you have configure you can use this */
101#if defined(HAVE_CONFIG_H)
102#include "config.h"
103#endif
1a3db59c 104
eee79a2e 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
cb69b4c7 113
114#include "squid.h"
115
1a3db59c 116/* local constants */
117
118/* default values for buffer sizes, used by memBufDefInit */
119#define MEM_BUF_INIT_SIZE (2*1024)
b8890359 120#define MEM_BUF_MAX_SIZE (2*1000*1024*1024)
1a3db59c 121
122
cb69b4c7 123/* local routines */
2ac76861 124static void memBufGrow(MemBuf * mb, mb_size_t min_cap);
cb69b4c7 125
126
1a3db59c 127/* init with defaults */
128void
2ac76861 129memBufDefInit(MemBuf * mb)
1a3db59c 130{
131 memBufInit(mb, MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE);
132}
133
cb69b4c7 134
1a3db59c 135/* init with specific sizes */
cb69b4c7 136void
2ac76861 137memBufInit(MemBuf * mb, mb_size_t szInit, mb_size_t szMax)
cb69b4c7 138{
139 assert(mb);
140 assert(szInit > 0 && szMax > 0);
141
142 mb->buf = NULL;
143 mb->size = 0;
144 mb->max_capacity = szMax;
145 mb->capacity = 0;
1eb41ae8 146 mb->stolen = 0;
cb69b4c7 147
148 memBufGrow(mb, szInit);
149}
150
1a3db59c 151/*
152 * cleans the mb; last function to call if you do not give .buf away with
153 * memBufFreeFunc
154 */
cb69b4c7 155void
2ac76861 156memBufClean(MemBuf * mb)
cb69b4c7 157{
2ac76861 158 assert(mb);
159 assert(mb->buf);
1eb41ae8 160 assert(!mb->stolen); /* not frozen */
cb69b4c7 161
1eb41ae8 162 memFreeBuf(mb->capacity, mb->buf);
2ac76861 163 mb->buf = NULL;
4eb368f9 164 mb->size = mb->capacity = mb->max_capacity = 0;
cb69b4c7 165}
166
62e76326 167/* cleans the buffer without changing its capacity
137ee196 168 * if called with a Null buffer, calls memBufDefInit() */
169void
170memBufReset(MemBuf * mb)
171{
172 assert(mb);
173
1d21d91d 174 if (memBufIsNull(mb)) {
62e76326 175 memBufDefInit(mb);
137ee196 176 } else {
62e76326 177 assert(!mb->stolen); /* not frozen */
178 /* reset */
179 memset(mb->buf, 0, mb->capacity);
180 mb->size = 0;
137ee196 181 }
182}
183
b8890359 184/* unfortunate hack to test if the buffer has been Init()ialized */
1d21d91d 185int
186memBufIsNull(MemBuf * mb)
187{
188 assert(mb);
62e76326 189
1d21d91d 190 if (!mb->buf && !mb->max_capacity && !mb->capacity && !mb->size)
62e76326 191 return 1; /* is null (not initialized) */
192
0cdcddb9 193 assert(mb->buf && mb->max_capacity && mb->capacity); /* paranoid */
62e76326 194
1d21d91d 195 return 0;
196}
197
198
1a3db59c 199/* calls memcpy, appends exactly size bytes, extends buffer if needed */
cb69b4c7 200void
2ac76861 201memBufAppend(MemBuf * mb, const char *buf, mb_size_t sz)
cb69b4c7 202{
203 assert(mb && buf && sz >= 0);
204 assert(mb->buf);
1eb41ae8 205 assert(!mb->stolen); /* not frozen */
cb69b4c7 206
207 if (sz > 0) {
62e76326 208 if (mb->size + sz + 1 > mb->capacity)
209 memBufGrow(mb, mb->size + sz + 1);
210
211 assert(mb->size + sz <= mb->capacity); /* paranoid */
212
213 xmemcpy(mb->buf + mb->size, buf, sz);
214
215 mb->size += sz;
216
217 mb->buf[mb->size] = '\0'; /* \0 terminate in case we are used as a string. Not counted in the size */
cb69b4c7 218 }
219}
220
19ecac48 221/* calls memBufVPrintf */
6de2df60 222#if STDC_HEADERS
cb69b4c7 223void
2ac76861 224memBufPrintf(MemBuf * mb, const char *fmt,...)
cb69b4c7 225{
226 va_list args;
227 va_start(args, fmt);
228#else
229void
230memBufPrintf(va_alist)
62e76326 231va_dcl
cb69b4c7 232{
233 va_list args;
234 MemBuf *mb = NULL;
235 const char *fmt = NULL;
236 mb_size_t sz = 0;
237 va_start(args);
238 mb = va_arg(args, MemBuf *);
239 fmt = va_arg(args, char *);
240#endif
62e76326 241
cb69b4c7 242 memBufVPrintf(mb, fmt, args);
243 va_end(args);
244}
245
246
19ecac48 247/* vprintf for other printf()'s to use; calls vsnprintf, extends buf if needed */
cb69b4c7 248void
62e76326 249memBufVPrintf(MemBuf * mb, const char *fmt, va_list vargs) {
eee79a2e 250#ifdef VA_COPY
251 va_list ap;
252#endif
253
19ecac48 254 int sz = 0;
cb69b4c7 255 assert(mb && fmt);
256 assert(mb->buf);
1eb41ae8 257 assert(!mb->stolen); /* not frozen */
2246b732 258 /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
62e76326 259
2ac76861 260 while (mb->capacity <= mb->max_capacity) {
62e76326 261 mb_size_t free_space = mb->capacity - mb->size;
262 /* put as much as we can */
eee79a2e 263
264#ifdef VA_COPY
265 /* Fix of bug 753r. The value of vargs is undefined
266 * after vsnprintf() returns. Make a copy of vargs
267 * incase we loop around and call vsnprintf() again.
268 */
269 VA_COPY(ap,vargs);
270 sz = vsnprintf(mb->buf + mb->size, free_space, fmt, ap);
af32d1ae 271 va_end(ap);
eee79a2e 272#else /* VA_COPY */
273
62e76326 274 sz = vsnprintf(mb->buf + mb->size, free_space, fmt, vargs);
eee79a2e 275#endif /*VA_COPY*/
62e76326 276 /* check for possible overflow */
277 /* snprintf on Linuz returns -1 on overflows */
278 /* snprintf on FreeBSD returns at least free_space on overflows */
279
280 if (sz < 0 || sz >= free_space)
281 memBufGrow(mb, mb->capacity + 1);
282 else
283 break;
cb69b4c7 284 }
62e76326 285
19ecac48 286 mb->size += sz;
137ee196 287 /* on Linux and FreeBSD, '\0' is not counted in return value */
288 /* on XXX it might be counted */
289 /* check that '\0' is appended and not counted */
62e76326 290
43d2af69 291 if (!mb->size || mb->buf[mb->size - 1]) {
62e76326 292 assert(!mb->buf[mb->size]);
43d2af69 293 } else {
62e76326 294 mb->size--;
43d2af69 295 }
cb69b4c7 296}
297
1a3db59c 298/*
299 * returns free() function to be used.
300 * Important:
301 * calling this function "freezes" mb,
302 * do not _update_ mb after that in any way
303 * (you still can read-access .buf and .size)
304 */
cb69b4c7 305FREE *
62e76326 306memBufFreeFunc(MemBuf * mb) {
cb69b4c7 307 FREE *ff;
308 assert(mb);
309 assert(mb->buf);
1eb41ae8 310 assert(!mb->stolen); /* not frozen */
cb69b4c7 311
fa80a8ef 312 ff = memFreeBufFunc((size_t) mb->capacity);
313 mb->stolen = 1; /* freeze */
cb69b4c7 314 return ff;
315}
316
317/* grows (doubles) internal buffer to satisfy required minimal capacity */
318static void
62e76326 319memBufGrow(MemBuf * mb, mb_size_t min_cap) {
1eb41ae8 320 size_t new_cap;
321 size_t buf_cap;
137ee196 322
cb69b4c7 323 assert(mb);
1eb41ae8 324 assert(!mb->stolen);
cb69b4c7 325 assert(mb->capacity < min_cap);
326
327 /* determine next capacity */
62e76326 328
fa80a8ef 329 if (min_cap > 64 * 1024) {
62e76326 330 new_cap = 64 * 1024;
331
332 while (new_cap < (size_t) min_cap)
333 new_cap += 64 * 1024; /* increase in reasonable steps */
1eb41ae8 334 } else {
62e76326 335 new_cap = (size_t) min_cap;
1eb41ae8 336 }
cb69b4c7 337
338 /* last chance to fit before we assert(!overflow) */
fa80a8ef 339 if (new_cap > (size_t) mb->max_capacity)
62e76326 340 new_cap = (size_t) mb->max_capacity;
137ee196 341
fa80a8ef 342 assert(new_cap <= (size_t) mb->max_capacity); /* no overflow */
62e76326 343
fa80a8ef 344 assert(new_cap > (size_t) mb->capacity); /* progress */
1eb41ae8 345
fa80a8ef 346 buf_cap = (size_t) mb->capacity;
62e76326 347
e6ccf245 348 mb->buf = (char *)memReallocBuf(mb->buf, new_cap, &buf_cap);
137ee196 349
350 /* done */
fa80a8ef 351 mb->capacity = (mb_size_t) buf_cap;
cb69b4c7 352}
353
1a3db59c 354
355/* Reports */
356
357/* puts report on MemBuf _module_ usage into mb */
cb69b4c7 358void
62e76326 359memBufReport(MemBuf * mb) {
cb69b4c7 360 assert(mb);
361 memBufPrintf(mb, "memBufReport is not yet implemented @?@\n");
362}
528b2c61 363
364#ifndef _USE_INLINE_
365#include "MemBuf.cci"
366#endif