]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemBuf.cc
update
[thirdparty/squid.git] / src / MemBuf.cc
CommitLineData
cb69b4c7 1/*
19ecac48 2 * $Id: MemBuf.cc,v 1.11 1998/05/28 17:32:41 rousskov Exp $
cb69b4c7 3 *
123abbe1 4 * DEBUG: section 59 auto-growing Memory Buffer with printf
cb69b4c7 5 * AUTHOR: Alex Rousskov
6 *
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
8 * --------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by
13 * the National Science Foundation.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
29 */
30
cb69b4c7 31/*
2246b732 32 * To-Do: use memory pools for .buf recycling @?@ @?@
cb69b4c7 33 */
34
1a3db59c 35/*
2ac76861 36 * Rationale:
37 * ----------
38 *
39 * Here is how one would comm_write an object without MemBuffer:
40 *
41 * {
42 * -- allocate:
43 * buf = malloc(big_enough);
44 *
45 * -- "pack":
46 * snprintf object(s) piece-by-piece constantly checking for overflows
47 * and maintaining (buf+offset);
48 * ...
49 *
50 * -- write
51 * comm_write(buf, free, ...);
52 * }
53 *
54 * The whole "packing" idea is quite messy: We are given a buffer of fixed
55 * size and we have to check all the time that we still fit. Sounds logical.
56 * However, what happens if we have more data? If we are lucky to be careful
57 * to stop before we overrun any buffers, we still may have garbage (e.g.
58 * half of ETag) in the buffer.
59 *
60 * MemBuffer:
61 * ----------
62 *
63 * MemBuffer is a memory-resident buffer with printf()-like interface. It
64 * hides all offest handling and overflow checking. Moreover, it has a
65 * build-in control that no partial data has been written.
66 *
67 * MemBuffer is designed to handle relatively small data. It starts with a
68 * small buffer of configurable size to avoid allocating huge buffers all the
69 * time. MemBuffer doubles the buffer when needed. It assert()s that it will
70 * not grow larger than a configurable limit. MemBuffer has virtually no
71 * overhead (and can even reduce memory consumption) compared to old
72 * "packing" approach.
73 *
74 * MemBuffer eliminates both "packing" mess and truncated data:
75 *
76 * {
77 * -- setup
78 * MemBuf buf;
79 *
80 * -- required init with optional size tuning (see #defines for defaults)
81 * memBufInit(&buf, initial-size, absolute-maximum);
82 *
83 * -- "pack" (no need to handle offsets or check for overflows)
84 * memBufPrintf(&buf, ...);
85 * ...
86 *
87 * -- write
2246b732 88 * comm_write_mbuf(fd, buf, handler, data);
89 *
2ac76861 90 * -- *iff* you did not give the buffer away, free it yourself
51251ed1 91 * -- memBufClean(&buf);
2ac76861 92 * }
93 */
1a3db59c 94
cb69b4c7 95
96#include "squid.h"
97
1a3db59c 98/* local constants */
99
100/* default values for buffer sizes, used by memBufDefInit */
101#define MEM_BUF_INIT_SIZE (2*1024)
102#define MEM_BUF_MAX_SIZE (32*1024)
103
104
cb69b4c7 105/* local routines */
2ac76861 106static void memBufGrow(MemBuf * mb, mb_size_t min_cap);
cb69b4c7 107
108
1a3db59c 109/* init with defaults */
110void
2ac76861 111memBufDefInit(MemBuf * mb)
1a3db59c 112{
113 memBufInit(mb, MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE);
114}
115
cb69b4c7 116
1a3db59c 117/* init with specific sizes */
cb69b4c7 118void
2ac76861 119memBufInit(MemBuf * mb, mb_size_t szInit, mb_size_t szMax)
cb69b4c7 120{
121 assert(mb);
122 assert(szInit > 0 && szMax > 0);
123
124 mb->buf = NULL;
125 mb->size = 0;
126 mb->max_capacity = szMax;
127 mb->capacity = 0;
128 mb->freefunc = NULL;
129
130 memBufGrow(mb, szInit);
131}
132
1a3db59c 133/*
134 * cleans the mb; last function to call if you do not give .buf away with
135 * memBufFreeFunc
136 */
cb69b4c7 137void
2ac76861 138memBufClean(MemBuf * mb)
cb69b4c7 139{
2ac76861 140 assert(mb);
141 assert(mb->buf);
142 assert(mb->freefunc); /* not frozen */
cb69b4c7 143
2246b732 144 (*mb->freefunc) (mb->buf); /* free */
145 mb->freefunc = NULL; /* freeze */
2ac76861 146 mb->buf = NULL;
147 mb->size = mb->capacity = 0;
cb69b4c7 148}
149
1a3db59c 150/* calls memcpy, appends exactly size bytes, extends buffer if needed */
cb69b4c7 151void
2ac76861 152memBufAppend(MemBuf * mb, const char *buf, mb_size_t sz)
cb69b4c7 153{
154 assert(mb && buf && sz >= 0);
155 assert(mb->buf);
2ac76861 156 assert(mb->freefunc); /* not frozen */
cb69b4c7 157
158 if (sz > 0) {
159 if (mb->size + sz > mb->capacity)
2ac76861 160 memBufGrow(mb, mb->size + sz);
161 assert(mb->size + sz <= mb->capacity); /* paranoid */
cb69b4c7 162 xmemcpy(mb->buf + mb->size, buf, sz);
163 mb->size += sz;
164 }
165}
166
19ecac48 167/* calls memBufVPrintf */
cb69b4c7 168#ifdef __STDC__
169void
2ac76861 170memBufPrintf(MemBuf * mb, const char *fmt,...)
cb69b4c7 171{
172 va_list args;
173 va_start(args, fmt);
174#else
175void
176memBufPrintf(va_alist)
177 va_dcl
178{
179 va_list args;
180 MemBuf *mb = NULL;
181 const char *fmt = NULL;
182 mb_size_t sz = 0;
183 va_start(args);
184 mb = va_arg(args, MemBuf *);
185 fmt = va_arg(args, char *);
186#endif
187 memBufVPrintf(mb, fmt, args);
188 va_end(args);
189}
190
191
19ecac48 192/* vprintf for other printf()'s to use; calls vsnprintf, extends buf if needed */
cb69b4c7 193void
2ac76861 194memBufVPrintf(MemBuf * mb, const char *fmt, va_list vargs)
cb69b4c7 195{
19ecac48 196 int sz = 0;
cb69b4c7 197 assert(mb && fmt);
198 assert(mb->buf);
2ac76861 199 assert(mb->freefunc); /* not frozen */
cb69b4c7 200 /* @?@ we do not init buf with '\0', do we have to for vsnprintf?? @?@ */
2246b732 201 /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
2ac76861 202 while (mb->capacity <= mb->max_capacity) {
cb69b4c7 203 mb_size_t free_space = mb->capacity - mb->size;
204 /* put as much as we can */
19ecac48 205 sz = vsnprintf(mb->buf + mb->size, free_space, fmt, vargs);
206 /* check for possible overflow */
207 /* snprintf on Linuz returns -1 on overflows */
208 /* snprintf on FreeBSD returns at least free_space on overflows */
209 if (sz < 0 || sz + 32 >= free_space) /* magic constant 32, ARGH! @?@ */
2ac76861 210 memBufGrow(mb, mb->capacity + 1);
cb69b4c7 211 else
212 break;
213 }
19ecac48 214 /* snprintf on FreeBSD and linux do not count terminating '\0' as "character stored" */
215 if (!sz || mb->buf[mb->size+sz-1])
216 assert(!mb->buf[mb->size+sz]);
217 else
218 sz--; /* we cut 0-terminator as store does */
219 mb->size += sz;
cb69b4c7 220}
221
1a3db59c 222/*
223 * returns free() function to be used.
224 * Important:
225 * calling this function "freezes" mb,
226 * do not _update_ mb after that in any way
227 * (you still can read-access .buf and .size)
228 */
cb69b4c7 229FREE *
2ac76861 230memBufFreeFunc(MemBuf * mb)
cb69b4c7 231{
232 FREE *ff;
233 assert(mb);
234 assert(mb->buf);
2ac76861 235 assert(mb->freefunc); /* not frozen */
cb69b4c7 236
237 ff = mb->freefunc;
2ac76861 238 mb->freefunc = NULL; /* freeze */
cb69b4c7 239 return ff;
240}
241
242/* grows (doubles) internal buffer to satisfy required minimal capacity */
243static void
2ac76861 244memBufGrow(MemBuf * mb, mb_size_t min_cap)
cb69b4c7 245{
246 mb_size_t new_cap;
247 assert(mb);
248 assert(mb->capacity < min_cap);
249
250 /* determine next capacity */
251 new_cap = mb->capacity;
252 if (new_cap > 0)
2ac76861 253 while (new_cap < min_cap)
254 new_cap *= 2; /* double */
cb69b4c7 255 else
256 new_cap = min_cap;
257
258 /* last chance to fit before we assert(!overflow) */
259 if (new_cap > mb->max_capacity)
260 new_cap = mb->max_capacity;
261
2ac76861 262 assert(new_cap <= mb->max_capacity); /* no overflow */
263 assert(new_cap > mb->capacity); /* progress */
cb69b4c7 264
265 /* finally [re]allocate memory */
266 if (!mb->buf) {
267 mb->buf = xmalloc(new_cap);
268 mb->freefunc = &xfree;
269 } else {
5999b776 270 assert(mb->freefunc == &xfree); /* for now */
25b3fde9 271 mb->buf = xrealloc(mb->buf, new_cap);
cb69b4c7 272 }
2ac76861 273 memset(mb->buf + mb->size, 0, new_cap - mb->size); /* just in case */
cb69b4c7 274 mb->capacity = new_cap;
275}
276
1a3db59c 277
278/* Reports */
279
280/* puts report on MemBuf _module_ usage into mb */
cb69b4c7 281void
2ac76861 282memBufReport(MemBuf * mb)
cb69b4c7 283{
284 assert(mb);
285 memBufPrintf(mb, "memBufReport is not yet implemented @?@\n");
286}