]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/MemBuf.cc
2 * $Id: MemBuf.cc,v 1.17 1998/07/21 17:26:17 wessels Exp $
4 * DEBUG: section 59 auto-growing Memory Buffer with printf
5 * AUTHOR: Alex Rousskov
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
8 * --------------------------------------------------------
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.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 * To-Do: use memory pools for .buf recycling @?@ @?@
39 * Here is how one would comm_write an object without MemBuffer:
43 * buf = malloc(big_enough);
46 * snprintf object(s) piece-by-piece constantly checking for overflows
47 * and maintaining (buf+offset);
51 * comm_write(buf, free, ...);
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.
57 * However, what happens if we have more data? If we are lucky to stop before
58 * we overrun any buffers, we still may have garbage (e.g. half of ETag) in
64 * MemBuffer is a memory-resident buffer with printf()-like interface. It
65 * hides all offest handling and overflow checking. Moreover, it has a
66 * build-in control that no partial data has been written.
68 * MemBuffer is designed to handle relatively small data. It starts with a
69 * small buffer of configurable size to avoid allocating huge buffers all the
70 * time. MemBuffer doubles the buffer when needed. It assert()s that it will
71 * not grow larger than a configurable limit. MemBuffer has virtually no
72 * overhead (and can even reduce memory consumption) compared to old
75 * MemBuffer eliminates both "packing" mess and truncated data:
81 * -- required init with optional size tuning (see #defines for defaults)
82 * memBufInit(&buf, initial-size, absolute-maximum);
84 * -- "pack" (no need to handle offsets or check for overflows)
85 * memBufPrintf(&buf, ...);
89 * comm_write_mbuf(fd, buf, handler, data);
91 * -- *iff* you did not give the buffer away, free it yourself
92 * -- memBufClean(&buf);
101 /* default values for buffer sizes, used by memBufDefInit */
102 #define MEM_BUF_INIT_SIZE (2*1024)
103 #define MEM_BUF_MAX_SIZE (32*1024)
107 static void memBufGrow(MemBuf
* mb
, mb_size_t min_cap
);
110 /* init with defaults */
112 memBufDefInit(MemBuf
* mb
)
114 memBufInit(mb
, MEM_BUF_INIT_SIZE
, MEM_BUF_MAX_SIZE
);
118 /* init with specific sizes */
120 memBufInit(MemBuf
* mb
, mb_size_t szInit
, mb_size_t szMax
)
123 assert(szInit
> 0 && szMax
> 0);
127 mb
->max_capacity
= szMax
;
131 memBufGrow(mb
, szInit
);
135 * cleans the mb; last function to call if you do not give .buf away with
139 memBufClean(MemBuf
* mb
)
143 assert(mb
->freefunc
); /* not frozen */
145 (*mb
->freefunc
) (mb
->buf
); /* free */
146 mb
->freefunc
= NULL
; /* freeze */
148 mb
->size
= mb
->capacity
= 0;
151 /* cleans the buffer without changing its capacity
152 * if called with a Null buffer, calls memBufDefInit() */
154 memBufReset(MemBuf
* mb
)
158 if (memBufIsNull(mb
)) {
161 assert(mb
->freefunc
); /* not frozen */
163 memset(mb
->buf
, 0, mb
->capacity
);
168 /* unfirtunate hack to test if the buffer has been Init()ialized */
170 memBufIsNull(MemBuf
* mb
)
173 if (!mb
->buf
&& !mb
->max_capacity
&& !mb
->capacity
&& !mb
->size
)
174 return 1; /* null, not initialized */
175 assert(mb
->buf
&& mb
->max_capacity
&& mb
->capacity
); /* paranoid */
180 /* calls memcpy, appends exactly size bytes, extends buffer if needed */
182 memBufAppend(MemBuf
* mb
, const char *buf
, mb_size_t sz
)
184 assert(mb
&& buf
&& sz
>= 0);
186 assert(mb
->freefunc
); /* not frozen */
189 if (mb
->size
+ sz
> mb
->capacity
)
190 memBufGrow(mb
, mb
->size
+ sz
);
191 assert(mb
->size
+ sz
<= mb
->capacity
); /* paranoid */
192 xmemcpy(mb
->buf
+ mb
->size
, buf
, sz
);
197 /* calls memBufVPrintf */
200 memBufPrintf(MemBuf
* mb
, const char *fmt
,...)
206 memBufPrintf(va_alist
)
211 const char *fmt
= NULL
;
214 mb
= va_arg(args
, MemBuf
*);
215 fmt
= va_arg(args
, char *);
217 memBufVPrintf(mb
, fmt
, args
);
222 /* vprintf for other printf()'s to use; calls vsnprintf, extends buf if needed */
224 memBufVPrintf(MemBuf
* mb
, const char *fmt
, va_list vargs
)
229 assert(mb
->freefunc
); /* not frozen */
230 /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
231 while (mb
->capacity
<= mb
->max_capacity
) {
232 mb_size_t free_space
= mb
->capacity
- mb
->size
;
233 /* put as much as we can */
234 sz
= vsnprintf(mb
->buf
+ mb
->size
, free_space
, fmt
, vargs
);
235 /* check for possible overflow */
236 /* snprintf on Linuz returns -1 on overflows */
237 /* snprintf on FreeBSD returns at least free_space on overflows */
238 if (sz
< 0 || sz
>= free_space
)
239 memBufGrow(mb
, mb
->capacity
+ 1);
244 /* on Linux and FreeBSD, '\0' is not counted in return value */
245 /* on XXX it might be counted */
246 /* check that '\0' is appended and not counted */
247 if (!mb
->size
|| mb
->buf
[mb
->size
- 1])
248 assert(!mb
->buf
[mb
->size
]);
254 * returns free() function to be used.
256 * calling this function "freezes" mb,
257 * do not _update_ mb after that in any way
258 * (you still can read-access .buf and .size)
261 memBufFreeFunc(MemBuf
* mb
)
266 assert(mb
->freefunc
); /* not frozen */
269 mb
->freefunc
= NULL
; /* freeze */
273 /* grows (doubles) internal buffer to satisfy required minimal capacity */
275 memBufGrow(MemBuf
* mb
, mb_size_t min_cap
)
281 assert(mb
->capacity
< min_cap
);
283 /* determine next capacity */
284 new_cap
= mb
->capacity
;
286 while (new_cap
< min_cap
)
287 new_cap
*= 2; /* double */
291 /* last chance to fit before we assert(!overflow) */
292 if (new_cap
> mb
->max_capacity
)
293 new_cap
= mb
->max_capacity
;
295 assert(new_cap
<= mb
->max_capacity
); /* no overflow */
296 assert(new_cap
> mb
->capacity
); /* progress */
300 /* allocate new memory */
303 mb
->buf
= memAllocate(MEM_2K_BUF
);
304 mb
->freefunc
= &memFree2K
;
307 mb
->buf
= memAllocate(MEM_4K_BUF
);
308 mb
->freefunc
= &memFree4K
;
311 mb
->buf
= memAllocate(MEM_8K_BUF
);
312 mb
->freefunc
= &memFree8K
;
315 /* recycle if old buffer was not "pool"ed */
316 if (old_mb
.freefunc
== &xfree
) {
317 mb
->buf
= xrealloc(old_mb
.buf
, new_cap
);
319 old_mb
.freefunc
= NULL
;
320 /* init tail, just in case */
321 memset(mb
->buf
+ mb
->size
, 0, new_cap
- mb
->size
);
323 mb
->buf
= xcalloc(1, new_cap
);
324 mb
->freefunc
= &xfree
;
328 /* copy and free old buffer if needed */
329 if (old_mb
.buf
&& old_mb
.freefunc
) {
330 memcpy(mb
->buf
, old_mb
.buf
, old_mb
.size
);
331 (*old_mb
.freefunc
) (old_mb
.buf
);
333 assert(!old_mb
.buf
&& !old_mb
.freefunc
);
337 mb
->capacity
= new_cap
;
343 /* puts report on MemBuf _module_ usage into mb */
345 memBufReport(MemBuf
* mb
)
348 memBufPrintf(mb
, "memBufReport is not yet implemented @?@\n");