]> git.ipfire.org Git - thirdparty/squid.git/blame - src/MemBuf.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / MemBuf.cc
CommitLineData
cb69b4c7 1/*
bbc27441 2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
cb69b4c7 7 */
8
bbc27441 9/* DEBUG: section 59 auto-growing Memory Buffer with printf */
cb69b4c7 10
63be0a78 11/**
12 \verbatim
2ac76861 13 * Rationale:
14 * ----------
26ac0430 15 *
ec41b64c 16 * Here is how one would Comm::Write an object without MemBuffer:
26ac0430 17 *
2ac76861 18 * {
19 * -- allocate:
20 * buf = malloc(big_enough);
26ac0430 21 *
2ac76861 22 * -- "pack":
23 * snprintf object(s) piece-by-piece constantly checking for overflows
24 * and maintaining (buf+offset);
25 * ...
26ac0430 26 *
2ac76861 27 * -- write
ec41b64c 28 * Comm::Write(buf, free, ...);
2ac76861 29 * }
26ac0430 30 *
2ac76861 31 * The whole "packing" idea is quite messy: We are given a buffer of fixed
32 * size and we have to check all the time that we still fit. Sounds logical.
eb139d08 33 *
34 * However, what happens if we have more data? If we are lucky to stop before
35 * we overrun any buffers, we still may have garbage (e.g. half of ETag) in
36 * the buffer.
26ac0430 37 *
2ac76861 38 * MemBuffer:
39 * ----------
26ac0430 40 *
2ac76861 41 * MemBuffer is a memory-resident buffer with printf()-like interface. It
42 * hides all offest handling and overflow checking. Moreover, it has a
43 * build-in control that no partial data has been written.
26ac0430 44 *
2ac76861 45 * MemBuffer is designed to handle relatively small data. It starts with a
46 * small buffer of configurable size to avoid allocating huge buffers all the
47 * time. MemBuffer doubles the buffer when needed. It assert()s that it will
48 * not grow larger than a configurable limit. MemBuffer has virtually no
49 * overhead (and can even reduce memory consumption) compared to old
50 * "packing" approach.
26ac0430 51 *
2ac76861 52 * MemBuffer eliminates both "packing" mess and truncated data:
26ac0430 53 *
2ac76861 54 * {
55 * -- setup
56 * MemBuf buf;
26ac0430 57 *
2ac76861 58 * -- required init with optional size tuning (see #defines for defaults)
2fe7eff9 59 * buf.init(initial-size, absolute-maximum);
26ac0430 60 *
2ac76861 61 * -- "pack" (no need to handle offsets or check for overflows)
2fe7eff9 62 * buf.Printf(...);
2ac76861 63 * ...
26ac0430 64 *
2ac76861 65 * -- write
ec41b64c 66 * Comm::Write(fd, buf, callback);
2246b732 67 *
2ac76861 68 * -- *iff* you did not give the buffer away, free it yourself
2fe7eff9 69 * -- buf.clean();
2ac76861 70 * }
63be0a78 71 \endverbatim
2ac76861 72 */
63be0a78 73
f7f3304a 74#include "squid.h"
ed6e9fb9 75#include "mem/forward.h"
582c2af2
FC
76#include "MemBuf.h"
77#include "profiler/Profiler.h"
1a3db59c 78
eee79a2e 79#ifdef VA_COPY
80#undef VA_COPY
81#endif
82#if defined HAVE_VA_COPY
83#define VA_COPY va_copy
84#elif defined HAVE___VA_COPY
85#define VA_COPY __va_copy
86#endif
cb69b4c7 87
1a3db59c 88/* local constants */
89
90/* default values for buffer sizes, used by memBufDefInit */
91#define MEM_BUF_INIT_SIZE (2*1024)
b8890359 92#define MEM_BUF_MAX_SIZE (2*1000*1024*1024)
1a3db59c 93
2b418379 94CBDATA_CLASS_INIT(MemBuf);
cb69b4c7 95
63be0a78 96/** init with defaults */
1a3db59c 97void
2fe7eff9 98MemBuf::init()
1a3db59c 99{
2fe7eff9 100 init(MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE);
1a3db59c 101}
102
63be0a78 103/** init with specific sizes */
cb69b4c7 104void
2fe7eff9 105MemBuf::init(mb_size_t szInit, mb_size_t szMax)
cb69b4c7 106{
cb69b4c7 107 assert(szInit > 0 && szMax > 0);
2fe7eff9 108 buf = NULL;
109 size = 0;
110 max_capacity = szMax;
111 capacity = 0;
112 stolen = 0;
113 grow(szInit);
ae3a7e8a 114 terminate();
cb69b4c7 115}
116
63be0a78 117/**
1a3db59c 118 * cleans the mb; last function to call if you do not give .buf away with
119 * memBufFreeFunc
120 */
cb69b4c7 121void
2fe7eff9 122MemBuf::clean()
cb69b4c7 123{
2fe7eff9 124 if (isNull()) {
032785bf 125 // nothing to do
126 } else {
2fe7eff9 127 assert(buf);
f53969cc 128 assert(!stolen); /* not frozen */
032785bf 129
2fe7eff9 130 memFreeBuf(capacity, buf);
131 buf = NULL;
132 size = capacity = max_capacity = 0;
032785bf 133 }
cb69b4c7 134}
135
63be0a78 136/**
137 * Cleans the buffer without changing its capacity
138 * if called with a Null buffer, calls memBufDefInit()
139 */
137ee196 140void
2fe7eff9 141MemBuf::reset()
137ee196 142{
2fe7eff9 143 if (isNull()) {
144 init();
137ee196 145 } else {
f53969cc 146 assert(!stolen); /* not frozen */
62e76326 147 /* reset */
2fe7eff9 148 memset(buf, 0, capacity);
149 size = 0;
137ee196 150 }
151}
152
63be0a78 153/**
154 * Unfortunate hack to test if the buffer has been Init()ialized
155 */
1d21d91d 156int
2fe7eff9 157MemBuf::isNull()
1d21d91d 158{
2fe7eff9 159 if (!buf && !max_capacity && !capacity && !size)
f53969cc 160 return 1; /* is null (not initialized) */
62e76326 161
f53969cc 162 assert(buf && max_capacity && capacity); /* paranoid */
62e76326 163
1d21d91d 164 return 0;
165}
166
032785bf 167mb_size_t MemBuf::spaceSize() const
168{
169 const mb_size_t terminatedSize = size + 1;
170 return (terminatedSize < capacity) ? capacity - terminatedSize : 0;
171}
1d21d91d 172
032785bf 173mb_size_t MemBuf::potentialSpaceSize() const
cb69b4c7 174{
032785bf 175 const mb_size_t terminatedSize = size + 1;
176 return (terminatedSize < max_capacity) ? max_capacity - terminatedSize : 0;
177}
cb69b4c7 178
63be0a78 179/// removes sz bytes and "packs" by moving content left
032785bf 180void MemBuf::consume(mb_size_t shiftSize)
181{
182 const mb_size_t cSize = contentSize();
183 assert(0 <= shiftSize && shiftSize <= cSize);
184 assert(!stolen); /* not frozen */
62e76326 185
1d29274c 186 PROF_start(MemBuf_consume);
032785bf 187 if (shiftSize > 0) {
188 if (shiftSize < cSize)
41d00cd3 189 memmove(buf, buf + shiftSize, cSize - shiftSize);
62e76326 190
032785bf 191 size -= shiftSize;
62e76326 192
032785bf 193 terminate();
194 }
1d29274c 195 PROF_stop(MemBuf_consume);
032785bf 196}
197
1177ec50 198/// removes all whitespace prefix bytes and "packs" by moving content left
24eac830 199void MemBuf::consumeWhitespacePrefix()
1177ec50
AJ
200{
201 PROF_start(MemBuf_consumeWhitespace);
24eac830
AJ
202 if (contentSize() > 0) {
203 const char *end = buf + contentSize();
204 const char *p = buf;
1d972b9a 205 for (; p<end && xisspace(*p); ++p);
24eac830
AJ
206 if (p-buf > 0)
207 consume(p-buf);
208 }
1177ec50
AJ
209 PROF_stop(MemBuf_consumeWhitespace);
210}
211
821beb5e
AR
212// removes last tailSize bytes
213void MemBuf::truncate(mb_size_t tailSize)
214{
215 const mb_size_t cSize = contentSize();
216 assert(0 <= tailSize && tailSize <= cSize);
217 assert(!stolen); /* not frozen */
218 size -= tailSize;
219}
220
8fbd839c
AJ
221/**
222 * calls memcpy, appends exactly size bytes,
223 * extends buffer or creates buffer if needed.
224 */
032785bf 225void MemBuf::append(const char *newContent, mb_size_t sz)
226{
227 assert(sz >= 0);
8fbd839c 228 assert(buf || (0==capacity && 0==size));
032785bf 229 assert(!stolen); /* not frozen */
62e76326 230
1d29274c 231 PROF_start(MemBuf_append);
032785bf 232 if (sz > 0) {
233 if (size + sz + 1 > capacity)
2fe7eff9 234 grow(size + sz + 1);
032785bf 235
236 assert(size + sz <= capacity); /* paranoid */
41d00cd3 237 memcpy(space(), newContent, sz);
032785bf 238 appended(sz);
cb69b4c7 239 }
1d29274c 240 PROF_stop(MemBuf_append);
cb69b4c7 241}
242
8fbd839c 243/// updates content size after external append
032785bf 244void MemBuf::appended(mb_size_t sz)
245{
246 assert(size + sz <= capacity);
247 size += sz;
248 terminate();
249}
250
63be0a78 251/**
252 * Null-terminate in case we are used as a string.
253 * Extra octet is not counted in the content size (or space size)
254 *
255 \note XXX: but the extra octet is counted when growth decisions are made!
256 * This will cause the buffer to grow when spaceSize() == 1 on append,
257 * which will assert() if the buffer cannot grow any more.
258 */
032785bf 259void MemBuf::terminate()
260{
261 assert(size < capacity);
262 *space() = '\0';
263}
264
19ecac48 265/* calls memBufVPrintf */
cb69b4c7 266void
2fe7eff9 267MemBuf::Printf(const char *fmt,...)
cb69b4c7 268{
269 va_list args;
270 va_start(args, fmt);
2fe7eff9 271 vPrintf(fmt, args);
cb69b4c7 272 va_end(args);
273}
274
63be0a78 275/**
276 * vPrintf for other printf()'s to use; calls vsnprintf, extends buf if needed
277 */
cb69b4c7 278void
af6a12ee
AJ
279MemBuf::vPrintf(const char *fmt, va_list vargs)
280{
eee79a2e 281#ifdef VA_COPY
282 va_list ap;
283#endif
284
19ecac48 285 int sz = 0;
2fe7eff9 286 assert(fmt);
287 assert(buf);
f53969cc 288 assert(!stolen); /* not frozen */
2246b732 289 /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
62e76326 290
2fe7eff9 291 while (capacity <= max_capacity) {
292 mb_size_t free_space = capacity - size;
62e76326 293 /* put as much as we can */
eee79a2e 294
295#ifdef VA_COPY
296 /* Fix of bug 753r. The value of vargs is undefined
297 * after vsnprintf() returns. Make a copy of vargs
298 * incase we loop around and call vsnprintf() again.
299 */
300 VA_COPY(ap,vargs);
2fe7eff9 301 sz = vsnprintf(buf + size, free_space, fmt, ap);
af32d1ae 302 va_end(ap);
eee79a2e 303#else /* VA_COPY */
304
2fe7eff9 305 sz = vsnprintf(buf + size, free_space, fmt, vargs);
eee79a2e 306#endif /*VA_COPY*/
62e76326 307 /* check for possible overflow */
308 /* snprintf on Linuz returns -1 on overflows */
309 /* snprintf on FreeBSD returns at least free_space on overflows */
310
311 if (sz < 0 || sz >= free_space)
2fe7eff9 312 grow(capacity + 1);
62e76326 313 else
314 break;
cb69b4c7 315 }
62e76326 316
2fe7eff9 317 size += sz;
137ee196 318 /* on Linux and FreeBSD, '\0' is not counted in return value */
319 /* on XXX it might be counted */
320 /* check that '\0' is appended and not counted */
62e76326 321
2fe7eff9 322 if (!size || buf[size - 1]) {
323 assert(!buf[size]);
43d2af69 324 } else {
5e263176 325 --size;
43d2af69 326 }
cb69b4c7 327}
328
63be0a78 329/**
1a3db59c 330 * Important:
331 * calling this function "freezes" mb,
332 * do not _update_ mb after that in any way
333 * (you still can read-access .buf and .size)
63be0a78 334 *
335 \retval free() function to be used.
1a3db59c 336 */
cb69b4c7 337FREE *
af6a12ee
AJ
338MemBuf::freeFunc()
339{
cb69b4c7 340 FREE *ff;
2fe7eff9 341 assert(buf);
f53969cc 342 assert(!stolen); /* not frozen */
cb69b4c7 343
2fe7eff9 344 ff = memFreeBufFunc((size_t) capacity);
f53969cc 345 stolen = 1; /* freeze */
cb69b4c7 346 return ff;
347}
348
63be0a78 349/**
350 * Grows (doubles) internal buffer to satisfy required minimal capacity
351 */
2fe7eff9 352void
af6a12ee
AJ
353MemBuf::grow(mb_size_t min_cap)
354{
1eb41ae8 355 size_t new_cap;
356 size_t buf_cap;
137ee196 357
2fe7eff9 358 assert(!stolen);
359 assert(capacity < min_cap);
cb69b4c7 360
1d29274c 361 PROF_start(MemBuf_grow);
362
cb69b4c7 363 /* determine next capacity */
62e76326 364
fa80a8ef 365 if (min_cap > 64 * 1024) {
62e76326 366 new_cap = 64 * 1024;
367
368 while (new_cap < (size_t) min_cap)
f53969cc 369 new_cap += 64 * 1024; /* increase in reasonable steps */
1eb41ae8 370 } else {
62e76326 371 new_cap = (size_t) min_cap;
1eb41ae8 372 }
cb69b4c7 373
374 /* last chance to fit before we assert(!overflow) */
2fe7eff9 375 if (new_cap > (size_t) max_capacity)
376 new_cap = (size_t) max_capacity;
137ee196 377
f53969cc 378 assert(new_cap <= (size_t) max_capacity); /* no overflow */
62e76326 379
f53969cc 380 assert(new_cap > (size_t) capacity); /* progress */
1eb41ae8 381
2fe7eff9 382 buf_cap = (size_t) capacity;
62e76326 383
2fe7eff9 384 buf = (char *)memReallocBuf(buf, new_cap, &buf_cap);
137ee196 385
386 /* done */
2fe7eff9 387 capacity = (mb_size_t) buf_cap;
1d29274c 388 PROF_stop(MemBuf_grow);
cb69b4c7 389}
390
1a3db59c 391/* Reports */
392
63be0a78 393/**
394 * Puts report on MemBuf _module_ usage into mb
395 */
cb69b4c7 396void
af6a12ee
AJ
397memBufReport(MemBuf * mb)
398{
cb69b4c7 399 assert(mb);
2fe7eff9 400 mb->Printf("memBufReport is not yet implemented @?@\n");
cb69b4c7 401}
f53969cc 402