dynbuf.c (7016B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "../curl_setup.h" 26 #include "dynbuf.h" 27 #include "../curl_printf.h" 28 #ifdef BUILDING_LIBCURL 29 #include "../curl_memory.h" 30 #endif 31 #include "../memdebug.h" 32 33 #define MIN_FIRST_ALLOC 32 34 35 #ifdef DEBUGBUILD 36 #define DYNINIT 0xbee51da /* random pattern */ 37 #endif 38 39 /* 40 * Init a dynbuf struct. 41 */ 42 void curlx_dyn_init(struct dynbuf *s, size_t toobig) 43 { 44 DEBUGASSERT(s); 45 DEBUGASSERT(toobig); 46 DEBUGASSERT(toobig <= MAX_DYNBUF_SIZE); /* catch crazy mistakes */ 47 s->bufr = NULL; 48 s->leng = 0; 49 s->allc = 0; 50 s->toobig = toobig; 51 #ifdef DEBUGBUILD 52 s->init = DYNINIT; 53 #endif 54 } 55 56 /* 57 * free the buffer and re-init the necessary fields. It does not touch the 58 * 'init' field and thus this buffer can be reused to add data to again. 59 */ 60 void curlx_dyn_free(struct dynbuf *s) 61 { 62 DEBUGASSERT(s); 63 DEBUGASSERT(s->init == DYNINIT); 64 Curl_safefree(s->bufr); 65 s->leng = s->allc = 0; 66 } 67 68 /* 69 * Store/append an chunk of memory to the dynbuf. 70 */ 71 static CURLcode dyn_nappend(struct dynbuf *s, 72 const unsigned char *mem, size_t len) 73 { 74 size_t indx = s->leng; 75 size_t a = s->allc; 76 size_t fit = len + indx + 1; /* new string + old string + zero byte */ 77 78 /* try to detect if there is rubbish in the struct */ 79 DEBUGASSERT(s->init == DYNINIT); 80 DEBUGASSERT(s->toobig); 81 DEBUGASSERT(indx < s->toobig); 82 DEBUGASSERT(!s->leng || s->bufr); 83 DEBUGASSERT(a <= s->toobig); 84 DEBUGASSERT(!len || mem); 85 86 if(fit > s->toobig) { 87 curlx_dyn_free(s); 88 return CURLE_TOO_LARGE; 89 } 90 else if(!a) { 91 DEBUGASSERT(!indx); 92 /* first invoke */ 93 if(MIN_FIRST_ALLOC > s->toobig) 94 a = s->toobig; 95 else if(fit < MIN_FIRST_ALLOC) 96 a = MIN_FIRST_ALLOC; 97 else 98 a = fit; 99 } 100 else { 101 while(a < fit) 102 a *= 2; 103 if(a > s->toobig) 104 /* no point in allocating a larger buffer than this is allowed to use */ 105 a = s->toobig; 106 } 107 108 if(a != s->allc) { 109 /* this logic is not using Curl_saferealloc() to make the tool not have to 110 include that as well when it uses this code */ 111 void *p = realloc(s->bufr, a); 112 if(!p) { 113 curlx_dyn_free(s); 114 return CURLE_OUT_OF_MEMORY; 115 } 116 s->bufr = p; 117 s->allc = a; 118 } 119 120 if(len) 121 memcpy(&s->bufr[indx], mem, len); 122 s->leng = indx + len; 123 s->bufr[s->leng] = 0; 124 return CURLE_OK; 125 } 126 127 /* 128 * Clears the string, keeps the allocation. This can also be called on a 129 * buffer that already was freed. 130 */ 131 void curlx_dyn_reset(struct dynbuf *s) 132 { 133 DEBUGASSERT(s); 134 DEBUGASSERT(s->init == DYNINIT); 135 DEBUGASSERT(!s->leng || s->bufr); 136 if(s->leng) 137 s->bufr[0] = 0; 138 s->leng = 0; 139 } 140 141 /* 142 * Specify the size of the tail to keep (number of bytes from the end of the 143 * buffer). The rest will be dropped. 144 */ 145 CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail) 146 { 147 DEBUGASSERT(s); 148 DEBUGASSERT(s->init == DYNINIT); 149 DEBUGASSERT(!s->leng || s->bufr); 150 if(trail > s->leng) 151 return CURLE_BAD_FUNCTION_ARGUMENT; 152 else if(trail == s->leng) 153 return CURLE_OK; 154 else if(!trail) { 155 curlx_dyn_reset(s); 156 } 157 else { 158 memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail); 159 s->leng = trail; 160 s->bufr[s->leng] = 0; 161 } 162 return CURLE_OK; 163 164 } 165 166 /* 167 * Appends a buffer with length. 168 */ 169 CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len) 170 { 171 DEBUGASSERT(s); 172 DEBUGASSERT(s->init == DYNINIT); 173 DEBUGASSERT(!s->leng || s->bufr); 174 return dyn_nappend(s, mem, len); 175 } 176 177 /* 178 * Append a null-terminated string at the end. 179 */ 180 CURLcode curlx_dyn_add(struct dynbuf *s, const char *str) 181 { 182 size_t n; 183 DEBUGASSERT(str); 184 DEBUGASSERT(s); 185 DEBUGASSERT(s->init == DYNINIT); 186 DEBUGASSERT(!s->leng || s->bufr); 187 n = strlen(str); 188 return dyn_nappend(s, (const unsigned char *)str, n); 189 } 190 191 /* 192 * Append a string vprintf()-style 193 */ 194 CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) 195 { 196 #ifdef BUILDING_LIBCURL 197 int rc; 198 DEBUGASSERT(s); 199 DEBUGASSERT(s->init == DYNINIT); 200 DEBUGASSERT(!s->leng || s->bufr); 201 DEBUGASSERT(fmt); 202 rc = curlx_dyn_vprintf(s, fmt, ap); 203 204 if(!rc) 205 return CURLE_OK; 206 else if(rc == MERR_TOO_LARGE) 207 return CURLE_TOO_LARGE; 208 return CURLE_OUT_OF_MEMORY; 209 #else 210 char *str; 211 str = curl_mvaprintf(fmt, ap); /* this allocs a new string to append */ 212 213 if(str) { 214 CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str)); 215 free(str); 216 return result; 217 } 218 /* If we failed, we cleanup the whole buffer and return error */ 219 curlx_dyn_free(s); 220 return CURLE_OUT_OF_MEMORY; 221 #endif 222 } 223 224 /* 225 * Append a string printf()-style 226 */ 227 CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...) 228 { 229 CURLcode result; 230 va_list ap; 231 DEBUGASSERT(s); 232 DEBUGASSERT(s->init == DYNINIT); 233 DEBUGASSERT(!s->leng || s->bufr); 234 va_start(ap, fmt); 235 result = curlx_dyn_vaddf(s, fmt, ap); 236 va_end(ap); 237 return result; 238 } 239 240 /* 241 * Returns a pointer to the buffer. 242 */ 243 char *curlx_dyn_ptr(const struct dynbuf *s) 244 { 245 DEBUGASSERT(s); 246 DEBUGASSERT(s->init == DYNINIT); 247 DEBUGASSERT(!s->leng || s->bufr); 248 return s->bufr; 249 } 250 251 char *curlx_dyn_take(struct dynbuf *s, size_t *plen) 252 { 253 char *ptr = s->bufr; 254 DEBUGASSERT(s); 255 DEBUGASSERT(s->init == DYNINIT); 256 *plen = s->leng; 257 s->bufr = NULL; 258 s->leng = 0; 259 s->allc = 0; 260 return ptr; 261 } 262 263 /* 264 * Returns an unsigned pointer to the buffer. 265 */ 266 unsigned char *curlx_dyn_uptr(const struct dynbuf *s) 267 { 268 DEBUGASSERT(s); 269 DEBUGASSERT(s->init == DYNINIT); 270 DEBUGASSERT(!s->leng || s->bufr); 271 return (unsigned char *)s->bufr; 272 } 273 274 /* 275 * Returns the length of the buffer. 276 */ 277 size_t curlx_dyn_len(const struct dynbuf *s) 278 { 279 DEBUGASSERT(s); 280 DEBUGASSERT(s->init == DYNINIT); 281 DEBUGASSERT(!s->leng || s->bufr); 282 return s->leng; 283 } 284 285 /* 286 * Set a new (smaller) length. 287 */ 288 CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set) 289 { 290 DEBUGASSERT(s); 291 DEBUGASSERT(s->init == DYNINIT); 292 DEBUGASSERT(!s->leng || s->bufr); 293 if(set > s->leng) 294 return CURLE_BAD_FUNCTION_ARGUMENT; 295 s->leng = set; 296 s->bufr[s->leng] = 0; 297 return CURLE_OK; 298 }