]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/MemBuf.cc
4 * DEBUG: section 59 auto-growing Memory Buffer with printf
5 * AUTHOR: Alex Rousskov
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 \todo 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 * buf.init(initial-size, absolute-maximum);
89 * -- "pack" (no need to handle offsets or check for overflows)
94 * Comm::Write(fd, buf, callback);
96 * -- *iff* you did not give the buffer away, free it yourself
102 /* if you have configure you can use this */
103 #if defined(HAVE_CONFIG_H)
110 #if defined HAVE_VA_COPY
111 #define VA_COPY va_copy
112 #elif defined HAVE___VA_COPY
113 #define VA_COPY __va_copy
116 #include "squid-old.h"
119 /* local constants */
121 /* default values for buffer sizes, used by memBufDefInit */
122 #define MEM_BUF_INIT_SIZE (2*1024)
123 #define MEM_BUF_MAX_SIZE (2*1000*1024*1024)
125 CBDATA_CLASS_INIT(MemBuf
);
127 /** init with defaults */
131 init(MEM_BUF_INIT_SIZE
, MEM_BUF_MAX_SIZE
);
135 /** init with specific sizes */
137 MemBuf::init(mb_size_t szInit
, mb_size_t szMax
)
139 assert(szInit
> 0 && szMax
> 0);
142 max_capacity
= szMax
;
149 * cleans the mb; last function to call if you do not give .buf away with
159 assert(!stolen
); /* not frozen */
161 memFreeBuf(capacity
, buf
);
163 size
= capacity
= max_capacity
= 0;
168 * Cleans the buffer without changing its capacity
169 * if called with a Null buffer, calls memBufDefInit()
177 assert(!stolen
); /* not frozen */
179 memset(buf
, 0, capacity
);
185 * Unfortunate hack to test if the buffer has been Init()ialized
190 if (!buf
&& !max_capacity
&& !capacity
&& !size
)
191 return 1; /* is null (not initialized) */
193 assert(buf
&& max_capacity
&& capacity
); /* paranoid */
198 mb_size_t
MemBuf::spaceSize() const
200 const mb_size_t terminatedSize
= size
+ 1;
201 return (terminatedSize
< capacity
) ? capacity
- terminatedSize
: 0;
204 mb_size_t
MemBuf::potentialSpaceSize() const
206 const mb_size_t terminatedSize
= size
+ 1;
207 return (terminatedSize
< max_capacity
) ? max_capacity
- terminatedSize
: 0;
210 /// removes sz bytes and "packs" by moving content left
211 void MemBuf::consume(mb_size_t shiftSize
)
213 const mb_size_t cSize
= contentSize();
214 assert(0 <= shiftSize
&& shiftSize
<= cSize
);
215 assert(!stolen
); /* not frozen */
217 PROF_start(MemBuf_consume
);
219 if (shiftSize
< cSize
)
220 memmove(buf
, buf
+ shiftSize
, cSize
- shiftSize
);
226 PROF_stop(MemBuf_consume
);
229 // removes last tailSize bytes
230 void MemBuf::truncate(mb_size_t tailSize
)
232 const mb_size_t cSize
= contentSize();
233 assert(0 <= tailSize
&& tailSize
<= cSize
);
234 assert(!stolen
); /* not frozen */
239 * calls memcpy, appends exactly size bytes,
240 * extends buffer or creates buffer if needed.
242 void MemBuf::append(const char *newContent
, mb_size_t sz
)
245 assert(buf
|| (0==capacity
&& 0==size
));
246 assert(!stolen
); /* not frozen */
248 PROF_start(MemBuf_append
);
250 if (size
+ sz
+ 1 > capacity
)
253 assert(size
+ sz
<= capacity
); /* paranoid */
254 memcpy(space(), newContent
, sz
);
257 PROF_stop(MemBuf_append
);
260 /// updates content size after external append
261 void MemBuf::appended(mb_size_t sz
)
263 assert(size
+ sz
<= capacity
);
269 * Null-terminate in case we are used as a string.
270 * Extra octet is not counted in the content size (or space size)
272 \note XXX: but the extra octet is counted when growth decisions are made!
273 * This will cause the buffer to grow when spaceSize() == 1 on append,
274 * which will assert() if the buffer cannot grow any more.
276 void MemBuf::terminate()
278 assert(size
< capacity
);
282 /* calls memBufVPrintf */
284 MemBuf::Printf(const char *fmt
,...)
294 * vPrintf for other printf()'s to use; calls vsnprintf, extends buf if needed
297 MemBuf::vPrintf(const char *fmt
, va_list vargs
)
306 assert(!stolen
); /* not frozen */
307 /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
309 while (capacity
<= max_capacity
) {
310 mb_size_t free_space
= capacity
- size
;
311 /* put as much as we can */
314 /* Fix of bug 753r. The value of vargs is undefined
315 * after vsnprintf() returns. Make a copy of vargs
316 * incase we loop around and call vsnprintf() again.
319 sz
= vsnprintf(buf
+ size
, free_space
, fmt
, ap
);
323 sz
= vsnprintf(buf
+ size
, free_space
, fmt
, vargs
);
325 /* check for possible overflow */
326 /* snprintf on Linuz returns -1 on overflows */
327 /* snprintf on FreeBSD returns at least free_space on overflows */
329 if (sz
< 0 || sz
>= free_space
)
336 /* on Linux and FreeBSD, '\0' is not counted in return value */
337 /* on XXX it might be counted */
338 /* check that '\0' is appended and not counted */
340 if (!size
|| buf
[size
- 1]) {
349 * calling this function "freezes" mb,
350 * do not _update_ mb after that in any way
351 * (you still can read-access .buf and .size)
353 \retval free() function to be used.
360 assert(!stolen
); /* not frozen */
362 ff
= memFreeBufFunc((size_t) capacity
);
363 stolen
= 1; /* freeze */
368 * Grows (doubles) internal buffer to satisfy required minimal capacity
371 MemBuf::grow(mb_size_t min_cap
)
377 assert(capacity
< min_cap
);
379 PROF_start(MemBuf_grow
);
381 /* determine next capacity */
383 if (min_cap
> 64 * 1024) {
386 while (new_cap
< (size_t) min_cap
)
387 new_cap
+= 64 * 1024; /* increase in reasonable steps */
389 new_cap
= (size_t) min_cap
;
392 /* last chance to fit before we assert(!overflow) */
393 if (new_cap
> (size_t) max_capacity
)
394 new_cap
= (size_t) max_capacity
;
396 assert(new_cap
<= (size_t) max_capacity
); /* no overflow */
398 assert(new_cap
> (size_t) capacity
); /* progress */
400 buf_cap
= (size_t) capacity
;
402 buf
= (char *)memReallocBuf(buf
, new_cap
, &buf_cap
);
405 capacity
= (mb_size_t
) buf_cap
;
406 PROF_stop(MemBuf_grow
);
413 * Puts report on MemBuf _module_ usage into mb
416 memBufReport(MemBuf
* mb
)
419 mb
->Printf("memBufReport is not yet implemented @?@\n");
423 #include "MemBuf.cci"