]>
Commit | Line | Data |
---|---|---|
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 | 94 | CBDATA_CLASS_INIT(MemBuf); |
cb69b4c7 | 95 | |
63be0a78 | 96 | /** init with defaults */ |
1a3db59c | 97 | void |
2fe7eff9 | 98 | MemBuf::init() |
1a3db59c | 99 | { |
2fe7eff9 | 100 | init(MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE); |
1a3db59c | 101 | } |
102 | ||
63be0a78 | 103 | /** init with specific sizes */ |
cb69b4c7 | 104 | void |
2fe7eff9 | 105 | MemBuf::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 | 121 | void |
2fe7eff9 | 122 | MemBuf::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 | 140 | void |
2fe7eff9 | 141 | MemBuf::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 | 156 | int |
2fe7eff9 | 157 | MemBuf::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 | 167 | mb_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 | 173 | mb_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 | 180 | void 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 | 199 | void 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 |
213 | void 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 | 225 | void 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 | 244 | void 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 | 259 | void MemBuf::terminate() |
260 | { | |
261 | assert(size < capacity); | |
262 | *space() = '\0'; | |
263 | } | |
264 | ||
19ecac48 | 265 | /* calls memBufVPrintf */ |
cb69b4c7 | 266 | void |
2fe7eff9 | 267 | MemBuf::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 | 278 | void |
af6a12ee AJ |
279 | MemBuf::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 | 337 | FREE * |
af6a12ee AJ |
338 | MemBuf::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 | 352 | void |
af6a12ee AJ |
353 | MemBuf::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 | 396 | void |
af6a12ee AJ |
397 | memBufReport(MemBuf * mb) |
398 | { | |
cb69b4c7 | 399 | assert(mb); |
2fe7eff9 | 400 | mb->Printf("memBufReport is not yet implemented @?@\n"); |
cb69b4c7 | 401 | } |
f53969cc | 402 |