]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/MemBuf.cc
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
14 * Here is how one would Comm::Write an object without MemBuffer:
18 * buf = malloc(big_enough);
21 * snprintf object(s) piece-by-piece constantly checking for overflows
22 * and maintaining (buf+offset);
26 * Comm::Write(buf, free, ...);
29 * The whole "packing" idea is quite messy: We are given a buffer of fixed
30 * size and we have to check all the time that we still fit. Sounds logical.
32 * However, what happens if we have more data? If we are lucky to stop before
33 * we overrun any buffers, we still may have garbage (e.g. half of ETag) in
39 * MemBuffer is a memory-resident buffer with printf()-like interface. It
40 * hides all offset handling and overflow checking. Moreover, it has a
41 * built-in control that no partial data has been written.
43 * MemBuffer is designed to handle relatively small data. It starts with a
44 * small buffer of configurable size to avoid allocating huge buffers all the
45 * time. MemBuffer doubles the buffer when needed. It assert()s that it will
46 * not grow larger than a configurable limit. MemBuffer has virtually no
47 * overhead (and can even reduce memory consumption) compared to old
50 * MemBuffer eliminates both "packing" mess and truncated data:
56 * -- required init with optional size tuning (see #defines for defaults)
57 * buf.init(initial-size, absolute-maximum);
59 * -- "pack" (no need to handle offsets or check for overflows)
64 * Comm::Write(fd, buf, callback);
66 * -- *iff* you did not give the buffer away, free it yourself
73 #include "mem/forward.h"
78 /* default values for buffer sizes, used by memBufDefInit */
79 #define MEM_BUF_INIT_SIZE (2*1024)
80 #define MEM_BUF_MAX_SIZE (2*1000*1024*1024)
82 CBDATA_CLASS_INIT(MemBuf
);
84 /** init with defaults */
88 init(MEM_BUF_INIT_SIZE
, MEM_BUF_MAX_SIZE
);
91 /** init with specific sizes */
93 MemBuf::init(mb_size_t szInit
, mb_size_t szMax
)
95 assert(szInit
> 0 && szMax
> 0);
106 * cleans the mb; last function to call if you do not give .buf away with
116 assert(!stolen
); /* not frozen */
118 memFreeBuf(capacity
, buf
);
120 size
= capacity
= max_capacity
= 0;
125 * Cleans the buffer without changing its capacity
126 * if called with a Null buffer, calls memBufDefInit()
134 assert(!stolen
); /* not frozen */
136 memset(buf
, 0, capacity
);
142 * Unfortunate hack to test if the buffer has been Init()ialized
145 MemBuf::isNull() const
147 if (!buf
&& !max_capacity
&& !capacity
&& !size
)
148 return 1; /* is null (not initialized) */
150 assert(buf
&& max_capacity
&& capacity
); /* paranoid */
155 mb_size_t
MemBuf::spaceSize() const
157 const mb_size_t terminatedSize
= size
+ 1;
158 return (terminatedSize
< capacity
) ? capacity
- terminatedSize
: 0;
161 mb_size_t
MemBuf::potentialSpaceSize() const
163 const mb_size_t terminatedSize
= size
+ 1;
164 return (terminatedSize
< max_capacity
) ? max_capacity
- terminatedSize
: 0;
167 /// removes sz bytes and "packs" by moving content left
168 void MemBuf::consume(mb_size_t shiftSize
)
170 const mb_size_t cSize
= contentSize();
171 assert(0 <= shiftSize
&& shiftSize
<= cSize
);
172 assert(!stolen
); /* not frozen */
175 if (shiftSize
< cSize
)
176 memmove(buf
, buf
+ shiftSize
, cSize
- shiftSize
);
184 /// removes all whitespace prefix bytes and "packs" by moving content left
185 void MemBuf::consumeWhitespacePrefix()
187 if (contentSize() > 0) {
188 const char *end
= buf
+ contentSize();
190 for (; p
<end
&& xisspace(*p
); ++p
);
196 // removes last tailSize bytes
197 void MemBuf::truncate(mb_size_t tailSize
)
199 const mb_size_t cSize
= contentSize();
200 assert(0 <= tailSize
&& tailSize
<= cSize
);
201 assert(!stolen
); /* not frozen */
206 * calls memcpy, appends exactly size bytes,
207 * extends buffer or creates buffer if needed.
209 void MemBuf::append(const char *newContent
, int sz
)
212 assert(buf
|| (0==capacity
&& 0==size
));
213 assert(!stolen
); /* not frozen */
216 if (size
+ sz
+ 1 > capacity
)
219 assert(size
+ sz
<= capacity
); /* paranoid */
220 memcpy(space(), newContent
, sz
);
225 /// updates content size after external append
226 void MemBuf::appended(mb_size_t sz
)
228 assert(size
+ sz
<= capacity
);
234 * Null-terminate in case we are used as a string.
235 * Extra octet is not counted in the content size (or space size)
237 \note XXX: but the extra octet is counted when growth decisions are made!
238 * This will cause the buffer to grow when spaceSize() == 1 on append,
239 * which will assert() if the buffer cannot grow any more.
241 void MemBuf::terminate()
243 assert(size
< capacity
);
248 * vappendf for other printf()'s to use; calls vsnprintf, extends buf if needed
251 MemBuf::vappendf(const char *fmt
, va_list vargs
)
256 assert(!stolen
); /* not frozen */
257 /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
259 while (capacity
<= max_capacity
) {
260 mb_size_t free_space
= capacity
- size
;
261 /* put as much as we can */
263 /* Fix of bug 753r. The value of vargs is undefined
264 * after vsnprintf() returns. Make a copy of vargs
265 * in case we loop around and call vsnprintf() again.
269 sz
= vsnprintf(buf
+ size
, free_space
, fmt
, ap
);
272 /* check for possible overflow */
273 /* snprintf on Linuz returns -1 on overflows */
274 /* snprintf on FreeBSD returns at least free_space on overflows */
276 if (sz
< 0 || sz
>= free_space
)
283 /* on Linux and FreeBSD, '\0' is not counted in return value */
284 /* on XXX it might be counted */
285 /* check that '\0' is appended and not counted */
287 if (!size
|| buf
[size
- 1]) {
296 * calling this function "freezes" mb,
297 * do not _update_ mb after that in any way
298 * (you still can read-access .buf and .size)
300 \retval free() function to be used.
307 assert(!stolen
); /* not frozen */
309 ff
= memFreeBufFunc((size_t) capacity
);
310 stolen
= 1; /* freeze */
315 * Grows (doubles) internal buffer to satisfy required minimal capacity
318 MemBuf::grow(mb_size_t min_cap
)
324 assert(capacity
< min_cap
);
326 /* determine next capacity */
328 if (min_cap
> 64 * 1024) {
331 while (new_cap
< (size_t) min_cap
)
332 new_cap
+= 64 * 1024; /* increase in reasonable steps */
334 new_cap
= (size_t) min_cap
;
337 /* last chance to fit before we assert(!overflow) */
338 if (new_cap
> (size_t) max_capacity
)
339 new_cap
= (size_t) max_capacity
;
341 assert(new_cap
<= (size_t) max_capacity
); /* no overflow */
343 assert(new_cap
> (size_t) capacity
); /* progress */
345 buf_cap
= (size_t) capacity
;
347 buf
= (char *)memReallocBuf(buf
, new_cap
, &buf_cap
);
350 capacity
= (mb_size_t
) buf_cap
;
356 * Puts report on MemBuf _module_ usage into mb
359 memBufReport(MemBuf
* mb
)
362 mb
->appendf("memBufReport is not yet implemented @?@\n");