]>
Commit | Line | Data |
---|---|---|
f740a279 | 1 | |
cb69b4c7 | 2 | /* |
528b2c61 | 3 | * $Id: MemBuf.cc,v 1.33 2003/01/23 00:37:14 robertc Exp $ |
cb69b4c7 | 4 | * |
123abbe1 | 5 | * DEBUG: section 59 auto-growing Memory Buffer with printf |
cb69b4c7 | 6 | * AUTHOR: Alex Rousskov |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
cb69b4c7 | 10 | * |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
cb69b4c7 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
cb69b4c7 | 34 | */ |
35 | ||
cb69b4c7 | 36 | /* |
2246b732 | 37 | * To-Do: use memory pools for .buf recycling @?@ @?@ |
cb69b4c7 | 38 | */ |
39 | ||
1a3db59c | 40 | /* |
2ac76861 | 41 | * Rationale: |
42 | * ---------- | |
43 | * | |
44 | * Here is how one would comm_write an object without MemBuffer: | |
45 | * | |
46 | * { | |
47 | * -- allocate: | |
48 | * buf = malloc(big_enough); | |
49 | * | |
50 | * -- "pack": | |
51 | * snprintf object(s) piece-by-piece constantly checking for overflows | |
52 | * and maintaining (buf+offset); | |
53 | * ... | |
54 | * | |
55 | * -- write | |
56 | * comm_write(buf, free, ...); | |
57 | * } | |
58 | * | |
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. | |
eb139d08 | 61 | * |
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 | |
64 | * the buffer. | |
2ac76861 | 65 | * |
66 | * MemBuffer: | |
67 | * ---------- | |
68 | * | |
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. | |
72 | * | |
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 | |
78 | * "packing" approach. | |
79 | * | |
80 | * MemBuffer eliminates both "packing" mess and truncated data: | |
81 | * | |
82 | * { | |
83 | * -- setup | |
84 | * MemBuf buf; | |
85 | * | |
86 | * -- required init with optional size tuning (see #defines for defaults) | |
87 | * memBufInit(&buf, initial-size, absolute-maximum); | |
88 | * | |
89 | * -- "pack" (no need to handle offsets or check for overflows) | |
90 | * memBufPrintf(&buf, ...); | |
91 | * ... | |
92 | * | |
93 | * -- write | |
2246b732 | 94 | * comm_write_mbuf(fd, buf, handler, data); |
95 | * | |
2ac76861 | 96 | * -- *iff* you did not give the buffer away, free it yourself |
51251ed1 | 97 | * -- memBufClean(&buf); |
2ac76861 | 98 | * } |
99 | */ | |
1a3db59c | 100 | |
cb69b4c7 | 101 | |
102 | #include "squid.h" | |
103 | ||
1a3db59c | 104 | /* local constants */ |
105 | ||
106 | /* default values for buffer sizes, used by memBufDefInit */ | |
107 | #define MEM_BUF_INIT_SIZE (2*1024) | |
b8890359 | 108 | #define MEM_BUF_MAX_SIZE (2*1000*1024*1024) |
1a3db59c | 109 | |
110 | ||
cb69b4c7 | 111 | /* local routines */ |
2ac76861 | 112 | static void memBufGrow(MemBuf * mb, mb_size_t min_cap); |
cb69b4c7 | 113 | |
114 | ||
1a3db59c | 115 | /* init with defaults */ |
116 | void | |
2ac76861 | 117 | memBufDefInit(MemBuf * mb) |
1a3db59c | 118 | { |
119 | memBufInit(mb, MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE); | |
120 | } | |
121 | ||
cb69b4c7 | 122 | |
1a3db59c | 123 | /* init with specific sizes */ |
cb69b4c7 | 124 | void |
2ac76861 | 125 | memBufInit(MemBuf * mb, mb_size_t szInit, mb_size_t szMax) |
cb69b4c7 | 126 | { |
127 | assert(mb); | |
128 | assert(szInit > 0 && szMax > 0); | |
129 | ||
130 | mb->buf = NULL; | |
131 | mb->size = 0; | |
132 | mb->max_capacity = szMax; | |
133 | mb->capacity = 0; | |
1eb41ae8 | 134 | mb->stolen = 0; |
cb69b4c7 | 135 | |
136 | memBufGrow(mb, szInit); | |
137 | } | |
138 | ||
1a3db59c | 139 | /* |
140 | * cleans the mb; last function to call if you do not give .buf away with | |
141 | * memBufFreeFunc | |
142 | */ | |
cb69b4c7 | 143 | void |
2ac76861 | 144 | memBufClean(MemBuf * mb) |
cb69b4c7 | 145 | { |
2ac76861 | 146 | assert(mb); |
147 | assert(mb->buf); | |
1eb41ae8 | 148 | assert(!mb->stolen); /* not frozen */ |
cb69b4c7 | 149 | |
1eb41ae8 | 150 | memFreeBuf(mb->capacity, mb->buf); |
2ac76861 | 151 | mb->buf = NULL; |
152 | mb->size = mb->capacity = 0; | |
cb69b4c7 | 153 | } |
154 | ||
137ee196 | 155 | /* cleans the buffer without changing its capacity |
156 | * if called with a Null buffer, calls memBufDefInit() */ | |
157 | void | |
158 | memBufReset(MemBuf * mb) | |
159 | { | |
160 | assert(mb); | |
161 | ||
1d21d91d | 162 | if (memBufIsNull(mb)) { |
137ee196 | 163 | memBufDefInit(mb); |
164 | } else { | |
1eb41ae8 | 165 | assert(!mb->stolen); /* not frozen */ |
137ee196 | 166 | /* reset */ |
167 | memset(mb->buf, 0, mb->capacity); | |
168 | mb->size = 0; | |
169 | } | |
170 | } | |
171 | ||
b8890359 | 172 | /* unfortunate hack to test if the buffer has been Init()ialized */ |
1d21d91d | 173 | int |
174 | memBufIsNull(MemBuf * mb) | |
175 | { | |
176 | assert(mb); | |
177 | if (!mb->buf && !mb->max_capacity && !mb->capacity && !mb->size) | |
b8890359 | 178 | return 1; /* is null (not initialized) */ |
0cdcddb9 | 179 | assert(mb->buf && mb->max_capacity && mb->capacity); /* paranoid */ |
1d21d91d | 180 | return 0; |
181 | } | |
182 | ||
183 | ||
1a3db59c | 184 | /* calls memcpy, appends exactly size bytes, extends buffer if needed */ |
cb69b4c7 | 185 | void |
2ac76861 | 186 | memBufAppend(MemBuf * mb, const char *buf, mb_size_t sz) |
cb69b4c7 | 187 | { |
188 | assert(mb && buf && sz >= 0); | |
189 | assert(mb->buf); | |
1eb41ae8 | 190 | assert(!mb->stolen); /* not frozen */ |
cb69b4c7 | 191 | |
192 | if (sz > 0) { | |
cc5bb70f | 193 | if (mb->size + sz + 1 > mb->capacity) |
194 | memBufGrow(mb, mb->size + sz + 1); | |
2ac76861 | 195 | assert(mb->size + sz <= mb->capacity); /* paranoid */ |
cb69b4c7 | 196 | xmemcpy(mb->buf + mb->size, buf, sz); |
197 | mb->size += sz; | |
cc5bb70f | 198 | mb->buf[mb->size] = '\0'; /* \0 terminate in case we are used as a string. Not counted in the size */ |
cb69b4c7 | 199 | } |
200 | } | |
201 | ||
19ecac48 | 202 | /* calls memBufVPrintf */ |
6de2df60 | 203 | #if STDC_HEADERS |
cb69b4c7 | 204 | void |
2ac76861 | 205 | memBufPrintf(MemBuf * mb, const char *fmt,...) |
cb69b4c7 | 206 | { |
207 | va_list args; | |
208 | va_start(args, fmt); | |
209 | #else | |
210 | void | |
211 | memBufPrintf(va_alist) | |
212 | va_dcl | |
213 | { | |
214 | va_list args; | |
215 | MemBuf *mb = NULL; | |
216 | const char *fmt = NULL; | |
217 | mb_size_t sz = 0; | |
218 | va_start(args); | |
219 | mb = va_arg(args, MemBuf *); | |
220 | fmt = va_arg(args, char *); | |
221 | #endif | |
222 | memBufVPrintf(mb, fmt, args); | |
223 | va_end(args); | |
224 | } | |
225 | ||
226 | ||
19ecac48 | 227 | /* vprintf for other printf()'s to use; calls vsnprintf, extends buf if needed */ |
cb69b4c7 | 228 | void |
2ac76861 | 229 | memBufVPrintf(MemBuf * mb, const char *fmt, va_list vargs) |
cb69b4c7 | 230 | { |
19ecac48 | 231 | int sz = 0; |
cb69b4c7 | 232 | assert(mb && fmt); |
233 | assert(mb->buf); | |
1eb41ae8 | 234 | assert(!mb->stolen); /* not frozen */ |
2246b732 | 235 | /* assert in Grow should quit first, but we do not want to have a scary infinite loop */ |
2ac76861 | 236 | while (mb->capacity <= mb->max_capacity) { |
cb69b4c7 | 237 | mb_size_t free_space = mb->capacity - mb->size; |
238 | /* put as much as we can */ | |
19ecac48 | 239 | sz = vsnprintf(mb->buf + mb->size, free_space, fmt, vargs); |
240 | /* check for possible overflow */ | |
241 | /* snprintf on Linuz returns -1 on overflows */ | |
242 | /* snprintf on FreeBSD returns at least free_space on overflows */ | |
137ee196 | 243 | if (sz < 0 || sz >= free_space) |
2ac76861 | 244 | memBufGrow(mb, mb->capacity + 1); |
cb69b4c7 | 245 | else |
246 | break; | |
247 | } | |
19ecac48 | 248 | mb->size += sz; |
137ee196 | 249 | /* on Linux and FreeBSD, '\0' is not counted in return value */ |
250 | /* on XXX it might be counted */ | |
251 | /* check that '\0' is appended and not counted */ | |
43d2af69 | 252 | if (!mb->size || mb->buf[mb->size - 1]) { |
137ee196 | 253 | assert(!mb->buf[mb->size]); |
43d2af69 | 254 | } else { |
137ee196 | 255 | mb->size--; |
43d2af69 | 256 | } |
cb69b4c7 | 257 | } |
258 | ||
1a3db59c | 259 | /* |
260 | * returns free() function to be used. | |
261 | * Important: | |
262 | * calling this function "freezes" mb, | |
263 | * do not _update_ mb after that in any way | |
264 | * (you still can read-access .buf and .size) | |
265 | */ | |
cb69b4c7 | 266 | FREE * |
2ac76861 | 267 | memBufFreeFunc(MemBuf * mb) |
cb69b4c7 | 268 | { |
269 | FREE *ff; | |
270 | assert(mb); | |
271 | assert(mb->buf); | |
1eb41ae8 | 272 | assert(!mb->stolen); /* not frozen */ |
cb69b4c7 | 273 | |
fa80a8ef | 274 | ff = memFreeBufFunc((size_t) mb->capacity); |
275 | mb->stolen = 1; /* freeze */ | |
cb69b4c7 | 276 | return ff; |
277 | } | |
278 | ||
279 | /* grows (doubles) internal buffer to satisfy required minimal capacity */ | |
280 | static void | |
2ac76861 | 281 | memBufGrow(MemBuf * mb, mb_size_t min_cap) |
cb69b4c7 | 282 | { |
1eb41ae8 | 283 | size_t new_cap; |
284 | size_t buf_cap; | |
137ee196 | 285 | |
cb69b4c7 | 286 | assert(mb); |
1eb41ae8 | 287 | assert(!mb->stolen); |
cb69b4c7 | 288 | assert(mb->capacity < min_cap); |
289 | ||
290 | /* determine next capacity */ | |
fa80a8ef | 291 | if (min_cap > 64 * 1024) { |
292 | new_cap = 64 * 1024; | |
293 | while (new_cap < (size_t) min_cap) | |
294 | new_cap += 64 * 1024; /* increase in reasonable steps */ | |
1eb41ae8 | 295 | } else { |
fa80a8ef | 296 | new_cap = (size_t) min_cap; |
1eb41ae8 | 297 | } |
cb69b4c7 | 298 | |
299 | /* last chance to fit before we assert(!overflow) */ | |
fa80a8ef | 300 | if (new_cap > (size_t) mb->max_capacity) |
301 | new_cap = (size_t) mb->max_capacity; | |
137ee196 | 302 | |
fa80a8ef | 303 | assert(new_cap <= (size_t) mb->max_capacity); /* no overflow */ |
304 | assert(new_cap > (size_t) mb->capacity); /* progress */ | |
1eb41ae8 | 305 | |
fa80a8ef | 306 | buf_cap = (size_t) mb->capacity; |
e6ccf245 | 307 | mb->buf = (char *)memReallocBuf(mb->buf, new_cap, &buf_cap); |
137ee196 | 308 | |
309 | /* done */ | |
fa80a8ef | 310 | mb->capacity = (mb_size_t) buf_cap; |
cb69b4c7 | 311 | } |
312 | ||
1a3db59c | 313 | |
314 | /* Reports */ | |
315 | ||
316 | /* puts report on MemBuf _module_ usage into mb */ | |
cb69b4c7 | 317 | void |
2ac76861 | 318 | memBufReport(MemBuf * mb) |
cb69b4c7 | 319 | { |
320 | assert(mb); | |
321 | memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); | |
322 | } | |
528b2c61 | 323 | |
324 | #ifndef _USE_INLINE_ | |
325 | #include "MemBuf.cci" | |
326 | #endif |