]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/MemBuf.cc
3 * $Id: MemBuf.cc,v 1.25 2000/06/06 19:34:30 hno Exp $
5 * DEBUG: section 59 auto-growing Memory Buffer with printf
6 * AUTHOR: Alex Rousskov
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * 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 * memBufInit(&buf, initial-size, absolute-maximum);
89 * -- "pack" (no need to handle offsets or check for overflows)
90 * memBufPrintf(&buf, ...);
94 * comm_write_mbuf(fd, buf, handler, data);
96 * -- *iff* you did not give the buffer away, free it yourself
97 * -- memBufClean(&buf);
104 /* local constants */
106 /* default values for buffer sizes, used by memBufDefInit */
107 #define MEM_BUF_INIT_SIZE (2*1024)
108 #define MEM_BUF_MAX_SIZE (2*1000*1024*1024)
112 static void memBufGrow(MemBuf
* mb
, mb_size_t min_cap
);
115 /* init with defaults */
117 memBufDefInit(MemBuf
* mb
)
119 memBufInit(mb
, MEM_BUF_INIT_SIZE
, MEM_BUF_MAX_SIZE
);
123 /* init with specific sizes */
125 memBufInit(MemBuf
* mb
, mb_size_t szInit
, mb_size_t szMax
)
128 assert(szInit
> 0 && szMax
> 0);
132 mb
->max_capacity
= szMax
;
136 memBufGrow(mb
, szInit
);
140 * cleans the mb; last function to call if you do not give .buf away with
144 memBufClean(MemBuf
* mb
)
148 assert(mb
->freefunc
); /* not frozen */
150 (*mb
->freefunc
) (mb
->buf
); /* free */
151 mb
->freefunc
= NULL
; /* freeze */
153 mb
->size
= mb
->capacity
= 0;
156 /* cleans the buffer without changing its capacity
157 * if called with a Null buffer, calls memBufDefInit() */
159 memBufReset(MemBuf
* mb
)
163 if (memBufIsNull(mb
)) {
166 assert(mb
->freefunc
); /* not frozen */
168 memset(mb
->buf
, 0, mb
->capacity
);
173 /* unfortunate hack to test if the buffer has been Init()ialized */
175 memBufIsNull(MemBuf
* mb
)
178 if (!mb
->buf
&& !mb
->max_capacity
&& !mb
->capacity
&& !mb
->size
)
179 return 1; /* is null (not initialized) */
180 assert(mb
->buf
&& mb
->max_capacity
&& mb
->capacity
); /* paranoid */
185 /* calls memcpy, appends exactly size bytes, extends buffer if needed */
187 memBufAppend(MemBuf
* mb
, const char *buf
, mb_size_t sz
)
189 assert(mb
&& buf
&& sz
>= 0);
191 assert(mb
->freefunc
); /* not frozen */
194 if (mb
->size
+ sz
> mb
->capacity
)
195 memBufGrow(mb
, mb
->size
+ sz
);
196 assert(mb
->size
+ sz
<= mb
->capacity
); /* paranoid */
197 xmemcpy(mb
->buf
+ mb
->size
, buf
, sz
);
202 /* calls memBufVPrintf */
205 memBufPrintf(MemBuf
* mb
, const char *fmt
,...)
211 memBufPrintf(va_alist
)
216 const char *fmt
= NULL
;
219 mb
= va_arg(args
, MemBuf
*);
220 fmt
= va_arg(args
, char *);
222 memBufVPrintf(mb
, fmt
, args
);
227 /* vprintf for other printf()'s to use; calls vsnprintf, extends buf if needed */
229 memBufVPrintf(MemBuf
* mb
, const char *fmt
, va_list vargs
)
234 assert(mb
->freefunc
); /* not frozen */
235 /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
236 while (mb
->capacity
<= mb
->max_capacity
) {
237 mb_size_t free_space
= mb
->capacity
- mb
->size
;
238 /* put as much as we can */
239 sz
= vsnprintf(mb
->buf
+ mb
->size
, free_space
, fmt
, vargs
);
240 /* check for possible overflow */
241 /* snprintf on Linuz returns -1 on overflows */
242 /* snprintf on FreeBSD returns at least free_space on overflows */
243 if (sz
< 0 || sz
>= free_space
)
244 memBufGrow(mb
, mb
->capacity
+ 1);
249 /* on Linux and FreeBSD, '\0' is not counted in return value */
250 /* on XXX it might be counted */
251 /* check that '\0' is appended and not counted */
252 if (!mb
->size
|| mb
->buf
[mb
->size
- 1]) {
253 assert(!mb
->buf
[mb
->size
]);
260 * returns free() function to be used.
262 * calling this function "freezes" mb,
263 * do not _update_ mb after that in any way
264 * (you still can read-access .buf and .size)
267 memBufFreeFunc(MemBuf
* mb
)
272 assert(mb
->freefunc
); /* not frozen */
275 mb
->freefunc
= NULL
; /* freeze */
279 /* grows (doubles) internal buffer to satisfy required minimal capacity */
281 memBufGrow(MemBuf
* mb
, mb_size_t min_cap
)
287 assert(mb
->capacity
< min_cap
);
289 /* determine next capacity */
290 new_cap
= mb
->capacity
;
292 while (new_cap
< min_cap
)
293 new_cap
*= 2; /* double */
297 /* last chance to fit before we assert(!overflow) */
298 if (new_cap
> mb
->max_capacity
)
299 new_cap
= mb
->max_capacity
;
301 assert(new_cap
<= mb
->max_capacity
); /* no overflow */
302 assert(new_cap
> mb
->capacity
); /* progress */
306 /* allocate new memory */
309 mb
->buf
= memAllocate(MEM_2K_BUF
);
310 mb
->freefunc
= &memFree2K
;
313 mb
->buf
= memAllocate(MEM_4K_BUF
);
314 mb
->freefunc
= &memFree4K
;
317 mb
->buf
= memAllocate(MEM_8K_BUF
);
318 mb
->freefunc
= &memFree8K
;
321 /* recycle if old buffer was not "pool"ed */
322 if (old_mb
.freefunc
== &xfree
) {
323 mb
->buf
= xrealloc(old_mb
.buf
, new_cap
);
325 old_mb
.freefunc
= NULL
;
326 /* init tail, just in case */
327 memset(mb
->buf
+ mb
->size
, 0, new_cap
- mb
->size
);
329 mb
->buf
= xcalloc(1, new_cap
);
330 mb
->freefunc
= &xfree
;
334 /* copy and free old buffer if needed */
335 if (old_mb
.buf
&& old_mb
.freefunc
) {
336 xmemcpy(mb
->buf
, old_mb
.buf
, old_mb
.size
);
337 (*old_mb
.freefunc
) (old_mb
.buf
);
339 assert(!old_mb
.buf
&& !old_mb
.freefunc
);
343 mb
->capacity
= new_cap
;
349 /* puts report on MemBuf _module_ usage into mb */
351 memBufReport(MemBuf
* mb
)
354 memBufPrintf(mb
, "memBufReport is not yet implemented @?@\n");