]> git.ipfire.org Git - thirdparty/squid.git/blob - src/MemBuf.cc
- Added binary http headers to requests
[thirdparty/squid.git] / src / MemBuf.cc
1 /*
2 * $Id: MemBuf.cc,v 1.8 1998/05/11 18:44:30 rousskov Exp $
3 *
4 * DEBUG: section 59 auto-growing Memory Buffer with printf
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
31 /*
32 * To-Do: uses memory pools for .buf recycling @?@ @?@
33 */
34
35 /*
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
88 * comm_write(buf.buf, memBufFreeFunc(&buf), ...);
89 *
90 * -- *iff* you did not give the buffer away, free it yourself
91 * -- memBufClean(&buf);
92 * }
93 */
94
95
96 #include "squid.h"
97
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
105 /* local routines */
106 static void memBufGrow(MemBuf * mb, mb_size_t min_cap);
107
108
109 /* init with defaults */
110 void
111 memBufDefInit(MemBuf * mb)
112 {
113 memBufInit(mb, MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE);
114 }
115
116
117 /* init with specific sizes */
118 void
119 memBufInit(MemBuf * mb, mb_size_t szInit, mb_size_t szMax)
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
133 /*
134 * cleans the mb; last function to call if you do not give .buf away with
135 * memBufFreeFunc
136 */
137 void
138 memBufClean(MemBuf * mb)
139 {
140 assert(mb);
141 assert(mb->buf);
142 assert(mb->freefunc); /* not frozen */
143
144 (*mb->freefunc) (mb->buf); /* freeze */
145 mb->buf = NULL;
146 mb->size = mb->capacity = 0;
147 }
148
149 /* calls memcpy, appends exactly size bytes, extends buffer if needed */
150 void
151 memBufAppend(MemBuf * mb, const char *buf, mb_size_t sz)
152 {
153 assert(mb && buf && sz >= 0);
154 assert(mb->buf);
155 assert(mb->freefunc); /* not frozen */
156
157 if (sz > 0) {
158 if (mb->size + sz > mb->capacity)
159 memBufGrow(mb, mb->size + sz);
160 assert(mb->size + sz <= mb->capacity); /* paranoid */
161 xmemcpy(mb->buf + mb->size, buf, sz);
162 mb->size += sz;
163 }
164 }
165
166 /* calls snprintf, extends buffer if needed */
167 #ifdef __STDC__
168 void
169 memBufPrintf(MemBuf * mb, const char *fmt,...)
170 {
171 va_list args;
172 va_start(args, fmt);
173 #else
174 void
175 memBufPrintf(va_alist)
176 va_dcl
177 {
178 va_list args;
179 MemBuf *mb = NULL;
180 const char *fmt = NULL;
181 mb_size_t sz = 0;
182 va_start(args);
183 mb = va_arg(args, MemBuf *);
184 fmt = va_arg(args, char *);
185 #endif
186 memBufVPrintf(mb, fmt, args);
187 va_end(args);
188 }
189
190
191 /* vprintf for other printf()'s to use */
192 void
193 memBufVPrintf(MemBuf * mb, const char *fmt, va_list vargs)
194 {
195 mb_size_t sz = 0;
196 assert(mb && fmt);
197 assert(mb->buf);
198 assert(mb->freefunc); /* not frozen */
199 /* @?@ we do not init buf with '\0', do we have to for vsnprintf?? @?@ */
200 /* assert in Grow should quit first, but we do not want to have a scare (1) loop */
201 while (mb->capacity <= mb->max_capacity) {
202 mb_size_t free_space = mb->capacity - mb->size;
203 /* put as much as we can */
204 sz = vsnprintf(mb->buf + mb->size, free_space, fmt, vargs) + 1;
205 /* check for possible overflow @?@ can vsnprintf cut more than needed off? */
206 if (sz + 32 >= free_space) /* magic constant 32, ARGH! @?@ */
207 memBufGrow(mb, mb->capacity + 1);
208 else
209 break;
210 }
211 mb->size += sz - 1; /* note that we cut 0-terminator as store does @?@ @?@ */
212 }
213
214 /*
215 * returns free() function to be used.
216 * Important:
217 * calling this function "freezes" mb,
218 * do not _update_ mb after that in any way
219 * (you still can read-access .buf and .size)
220 */
221 FREE *
222 memBufFreeFunc(MemBuf * mb)
223 {
224 FREE *ff;
225 assert(mb);
226 assert(mb->buf);
227 assert(mb->freefunc); /* not frozen */
228
229 ff = mb->freefunc;
230 mb->freefunc = NULL; /* freeze */
231 return ff;
232 }
233
234 /* grows (doubles) internal buffer to satisfy required minimal capacity */
235 static void
236 memBufGrow(MemBuf * mb, mb_size_t min_cap)
237 {
238 mb_size_t new_cap;
239 assert(mb);
240 assert(mb->capacity < min_cap);
241
242 /* determine next capacity */
243 new_cap = mb->capacity;
244 if (new_cap > 0)
245 while (new_cap < min_cap)
246 new_cap *= 2; /* double */
247 else
248 new_cap = min_cap;
249
250 /* last chance to fit before we assert(!overflow) */
251 if (new_cap > mb->max_capacity)
252 new_cap = mb->max_capacity;
253
254 assert(new_cap <= mb->max_capacity); /* no overflow */
255 assert(new_cap > mb->capacity); /* progress */
256
257 /* finally [re]allocate memory */
258 if (!mb->buf) {
259 mb->buf = xmalloc(new_cap);
260 mb->freefunc = &xfree;
261 } else {
262 assert(mb->freefunc == &xfree); /* for now */
263 mb->buf = xrealloc(mb->buf, new_cap);
264 }
265 memset(mb->buf + mb->size, 0, new_cap - mb->size); /* just in case */
266 mb->capacity = new_cap;
267 }
268
269
270 /* Reports */
271
272 /* puts report on MemBuf _module_ usage into mb */
273 void
274 memBufReport(MemBuf * mb)
275 {
276 assert(mb);
277 memBufPrintf(mb, "memBufReport is not yet implemented @?@\n");
278 }