]>
Commit | Line | Data |
---|---|---|
cb69b4c7 | 1 | /* |
19ecac48 | 2 | * $Id: MemBuf.cc,v 1.11 1998/05/28 17:32:41 rousskov Exp $ |
cb69b4c7 | 3 | * |
123abbe1 | 4 | * DEBUG: section 59 auto-growing Memory Buffer with printf |
cb69b4c7 | 5 | * AUTHOR: Alex Rousskov |
6 | * | |
7 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
8 | * -------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from the | |
11 | * Internet community. Development is led by Duane Wessels of the | |
12 | * National Laboratory for Applied Network Research and funded by | |
13 | * the National Science Foundation. | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
17 | * the Free Software Foundation; either version 2 of the License, or | |
18 | * (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, write to the Free Software | |
27 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
28 | * | |
29 | */ | |
30 | ||
cb69b4c7 | 31 | /* |
2246b732 | 32 | * To-Do: use memory pools for .buf recycling @?@ @?@ |
cb69b4c7 | 33 | */ |
34 | ||
1a3db59c | 35 | /* |
2ac76861 | 36 | * Rationale: |
37 | * ---------- | |
38 | * | |
39 | * Here is how one would comm_write an object without MemBuffer: | |
40 | * | |
41 | * { | |
42 | * -- allocate: | |
43 | * buf = malloc(big_enough); | |
44 | * | |
45 | * -- "pack": | |
46 | * snprintf object(s) piece-by-piece constantly checking for overflows | |
47 | * and maintaining (buf+offset); | |
48 | * ... | |
49 | * | |
50 | * -- write | |
51 | * comm_write(buf, free, ...); | |
52 | * } | |
53 | * | |
54 | * The whole "packing" idea is quite messy: We are given a buffer of fixed | |
55 | * size and we have to check all the time that we still fit. Sounds logical. | |
56 | * However, what happens if we have more data? If we are lucky to be careful | |
57 | * to stop before we overrun any buffers, we still may have garbage (e.g. | |
58 | * half of ETag) in the buffer. | |
59 | * | |
60 | * MemBuffer: | |
61 | * ---------- | |
62 | * | |
63 | * MemBuffer is a memory-resident buffer with printf()-like interface. It | |
64 | * hides all offest handling and overflow checking. Moreover, it has a | |
65 | * build-in control that no partial data has been written. | |
66 | * | |
67 | * MemBuffer is designed to handle relatively small data. It starts with a | |
68 | * small buffer of configurable size to avoid allocating huge buffers all the | |
69 | * time. MemBuffer doubles the buffer when needed. It assert()s that it will | |
70 | * not grow larger than a configurable limit. MemBuffer has virtually no | |
71 | * overhead (and can even reduce memory consumption) compared to old | |
72 | * "packing" approach. | |
73 | * | |
74 | * MemBuffer eliminates both "packing" mess and truncated data: | |
75 | * | |
76 | * { | |
77 | * -- setup | |
78 | * MemBuf buf; | |
79 | * | |
80 | * -- required init with optional size tuning (see #defines for defaults) | |
81 | * memBufInit(&buf, initial-size, absolute-maximum); | |
82 | * | |
83 | * -- "pack" (no need to handle offsets or check for overflows) | |
84 | * memBufPrintf(&buf, ...); | |
85 | * ... | |
86 | * | |
87 | * -- write | |
2246b732 | 88 | * comm_write_mbuf(fd, buf, handler, data); |
89 | * | |
2ac76861 | 90 | * -- *iff* you did not give the buffer away, free it yourself |
51251ed1 | 91 | * -- memBufClean(&buf); |
2ac76861 | 92 | * } |
93 | */ | |
1a3db59c | 94 | |
cb69b4c7 | 95 | |
96 | #include "squid.h" | |
97 | ||
1a3db59c | 98 | /* local constants */ |
99 | ||
100 | /* default values for buffer sizes, used by memBufDefInit */ | |
101 | #define MEM_BUF_INIT_SIZE (2*1024) | |
102 | #define MEM_BUF_MAX_SIZE (32*1024) | |
103 | ||
104 | ||
cb69b4c7 | 105 | /* local routines */ |
2ac76861 | 106 | static void memBufGrow(MemBuf * mb, mb_size_t min_cap); |
cb69b4c7 | 107 | |
108 | ||
1a3db59c | 109 | /* init with defaults */ |
110 | void | |
2ac76861 | 111 | memBufDefInit(MemBuf * mb) |
1a3db59c | 112 | { |
113 | memBufInit(mb, MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE); | |
114 | } | |
115 | ||
cb69b4c7 | 116 | |
1a3db59c | 117 | /* init with specific sizes */ |
cb69b4c7 | 118 | void |
2ac76861 | 119 | memBufInit(MemBuf * mb, mb_size_t szInit, mb_size_t szMax) |
cb69b4c7 | 120 | { |
121 | assert(mb); | |
122 | assert(szInit > 0 && szMax > 0); | |
123 | ||
124 | mb->buf = NULL; | |
125 | mb->size = 0; | |
126 | mb->max_capacity = szMax; | |
127 | mb->capacity = 0; | |
128 | mb->freefunc = NULL; | |
129 | ||
130 | memBufGrow(mb, szInit); | |
131 | } | |
132 | ||
1a3db59c | 133 | /* |
134 | * cleans the mb; last function to call if you do not give .buf away with | |
135 | * memBufFreeFunc | |
136 | */ | |
cb69b4c7 | 137 | void |
2ac76861 | 138 | memBufClean(MemBuf * mb) |
cb69b4c7 | 139 | { |
2ac76861 | 140 | assert(mb); |
141 | assert(mb->buf); | |
142 | assert(mb->freefunc); /* not frozen */ | |
cb69b4c7 | 143 | |
2246b732 | 144 | (*mb->freefunc) (mb->buf); /* free */ |
145 | mb->freefunc = NULL; /* freeze */ | |
2ac76861 | 146 | mb->buf = NULL; |
147 | mb->size = mb->capacity = 0; | |
cb69b4c7 | 148 | } |
149 | ||
1a3db59c | 150 | /* calls memcpy, appends exactly size bytes, extends buffer if needed */ |
cb69b4c7 | 151 | void |
2ac76861 | 152 | memBufAppend(MemBuf * mb, const char *buf, mb_size_t sz) |
cb69b4c7 | 153 | { |
154 | assert(mb && buf && sz >= 0); | |
155 | assert(mb->buf); | |
2ac76861 | 156 | assert(mb->freefunc); /* not frozen */ |
cb69b4c7 | 157 | |
158 | if (sz > 0) { | |
159 | if (mb->size + sz > mb->capacity) | |
2ac76861 | 160 | memBufGrow(mb, mb->size + sz); |
161 | assert(mb->size + sz <= mb->capacity); /* paranoid */ | |
cb69b4c7 | 162 | xmemcpy(mb->buf + mb->size, buf, sz); |
163 | mb->size += sz; | |
164 | } | |
165 | } | |
166 | ||
19ecac48 | 167 | /* calls memBufVPrintf */ |
cb69b4c7 | 168 | #ifdef __STDC__ |
169 | void | |
2ac76861 | 170 | memBufPrintf(MemBuf * mb, const char *fmt,...) |
cb69b4c7 | 171 | { |
172 | va_list args; | |
173 | va_start(args, fmt); | |
174 | #else | |
175 | void | |
176 | memBufPrintf(va_alist) | |
177 | va_dcl | |
178 | { | |
179 | va_list args; | |
180 | MemBuf *mb = NULL; | |
181 | const char *fmt = NULL; | |
182 | mb_size_t sz = 0; | |
183 | va_start(args); | |
184 | mb = va_arg(args, MemBuf *); | |
185 | fmt = va_arg(args, char *); | |
186 | #endif | |
187 | memBufVPrintf(mb, fmt, args); | |
188 | va_end(args); | |
189 | } | |
190 | ||
191 | ||
19ecac48 | 192 | /* vprintf for other printf()'s to use; calls vsnprintf, extends buf if needed */ |
cb69b4c7 | 193 | void |
2ac76861 | 194 | memBufVPrintf(MemBuf * mb, const char *fmt, va_list vargs) |
cb69b4c7 | 195 | { |
19ecac48 | 196 | int sz = 0; |
cb69b4c7 | 197 | assert(mb && fmt); |
198 | assert(mb->buf); | |
2ac76861 | 199 | assert(mb->freefunc); /* not frozen */ |
cb69b4c7 | 200 | /* @?@ we do not init buf with '\0', do we have to for vsnprintf?? @?@ */ |
2246b732 | 201 | /* assert in Grow should quit first, but we do not want to have a scary infinite loop */ |
2ac76861 | 202 | while (mb->capacity <= mb->max_capacity) { |
cb69b4c7 | 203 | mb_size_t free_space = mb->capacity - mb->size; |
204 | /* put as much as we can */ | |
19ecac48 | 205 | sz = vsnprintf(mb->buf + mb->size, free_space, fmt, vargs); |
206 | /* check for possible overflow */ | |
207 | /* snprintf on Linuz returns -1 on overflows */ | |
208 | /* snprintf on FreeBSD returns at least free_space on overflows */ | |
209 | if (sz < 0 || sz + 32 >= free_space) /* magic constant 32, ARGH! @?@ */ | |
2ac76861 | 210 | memBufGrow(mb, mb->capacity + 1); |
cb69b4c7 | 211 | else |
212 | break; | |
213 | } | |
19ecac48 | 214 | /* snprintf on FreeBSD and linux do not count terminating '\0' as "character stored" */ |
215 | if (!sz || mb->buf[mb->size+sz-1]) | |
216 | assert(!mb->buf[mb->size+sz]); | |
217 | else | |
218 | sz--; /* we cut 0-terminator as store does */ | |
219 | mb->size += sz; | |
cb69b4c7 | 220 | } |
221 | ||
1a3db59c | 222 | /* |
223 | * returns free() function to be used. | |
224 | * Important: | |
225 | * calling this function "freezes" mb, | |
226 | * do not _update_ mb after that in any way | |
227 | * (you still can read-access .buf and .size) | |
228 | */ | |
cb69b4c7 | 229 | FREE * |
2ac76861 | 230 | memBufFreeFunc(MemBuf * mb) |
cb69b4c7 | 231 | { |
232 | FREE *ff; | |
233 | assert(mb); | |
234 | assert(mb->buf); | |
2ac76861 | 235 | assert(mb->freefunc); /* not frozen */ |
cb69b4c7 | 236 | |
237 | ff = mb->freefunc; | |
2ac76861 | 238 | mb->freefunc = NULL; /* freeze */ |
cb69b4c7 | 239 | return ff; |
240 | } | |
241 | ||
242 | /* grows (doubles) internal buffer to satisfy required minimal capacity */ | |
243 | static void | |
2ac76861 | 244 | memBufGrow(MemBuf * mb, mb_size_t min_cap) |
cb69b4c7 | 245 | { |
246 | mb_size_t new_cap; | |
247 | assert(mb); | |
248 | assert(mb->capacity < min_cap); | |
249 | ||
250 | /* determine next capacity */ | |
251 | new_cap = mb->capacity; | |
252 | if (new_cap > 0) | |
2ac76861 | 253 | while (new_cap < min_cap) |
254 | new_cap *= 2; /* double */ | |
cb69b4c7 | 255 | else |
256 | new_cap = min_cap; | |
257 | ||
258 | /* last chance to fit before we assert(!overflow) */ | |
259 | if (new_cap > mb->max_capacity) | |
260 | new_cap = mb->max_capacity; | |
261 | ||
2ac76861 | 262 | assert(new_cap <= mb->max_capacity); /* no overflow */ |
263 | assert(new_cap > mb->capacity); /* progress */ | |
cb69b4c7 | 264 | |
265 | /* finally [re]allocate memory */ | |
266 | if (!mb->buf) { | |
267 | mb->buf = xmalloc(new_cap); | |
268 | mb->freefunc = &xfree; | |
269 | } else { | |
5999b776 | 270 | assert(mb->freefunc == &xfree); /* for now */ |
25b3fde9 | 271 | mb->buf = xrealloc(mb->buf, new_cap); |
cb69b4c7 | 272 | } |
2ac76861 | 273 | memset(mb->buf + mb->size, 0, new_cap - mb->size); /* just in case */ |
cb69b4c7 | 274 | mb->capacity = new_cap; |
275 | } | |
276 | ||
1a3db59c | 277 | |
278 | /* Reports */ | |
279 | ||
280 | /* puts report on MemBuf _module_ usage into mb */ | |
cb69b4c7 | 281 | void |
2ac76861 | 282 | memBufReport(MemBuf * mb) |
cb69b4c7 | 283 | { |
284 | assert(mb); | |
285 | memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); | |
286 | } |