xref: /curl/lib/dynbuf.c (revision 5b286c25)
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 #define DYNINIT 0xbee51da /* random pattern */
36 
37 /*
38  * Init a dynbuf struct.
39  */
Curl_dyn_init(struct dynbuf * s,size_t toobig)40 void Curl_dyn_init(struct dynbuf *s, size_t toobig)
41 {
42   DEBUGASSERT(s);
43   DEBUGASSERT(toobig);
44   s->bufr = NULL;
45   s->leng = 0;
46   s->allc = 0;
47   s->toobig = toobig;
48 #ifdef DEBUGBUILD
49   s->init = DYNINIT;
50 #endif
51 }
52 
53 /*
54  * free the buffer and re-init the necessary fields. It doesn't touch the
55  * 'init' field and thus this buffer can be reused to add data to again.
56  */
Curl_dyn_free(struct dynbuf * s)57 void Curl_dyn_free(struct dynbuf *s)
58 {
59   DEBUGASSERT(s);
60   Curl_safefree(s->bufr);
61   s->leng = s->allc = 0;
62 }
63 
64 /*
65  * Store/append an chunk of memory to the dynbuf.
66  */
dyn_nappend(struct dynbuf * s,const unsigned char * mem,size_t len)67 static CURLcode dyn_nappend(struct dynbuf *s,
68                             const unsigned char *mem, size_t len)
69 {
70   size_t indx = s->leng;
71   size_t a = s->allc;
72   size_t fit = len + indx + 1; /* new string + old string + zero byte */
73 
74   /* try to detect if there's rubbish in the struct */
75   DEBUGASSERT(s->init == DYNINIT);
76   DEBUGASSERT(s->toobig);
77   DEBUGASSERT(indx < s->toobig);
78   DEBUGASSERT(!s->leng || s->bufr);
79   DEBUGASSERT(a <= s->toobig);
80   DEBUGASSERT(!len || mem);
81 
82   if(fit > s->toobig) {
83     Curl_dyn_free(s);
84     return CURLE_TOO_LARGE;
85   }
86   else if(!a) {
87     DEBUGASSERT(!indx);
88     /* first invoke */
89     if(MIN_FIRST_ALLOC > s->toobig)
90       a = s->toobig;
91     else if(fit < MIN_FIRST_ALLOC)
92       a = MIN_FIRST_ALLOC;
93     else
94       a = fit;
95   }
96   else {
97     while(a < fit)
98       a *= 2;
99     if(a > s->toobig)
100       /* no point in allocating a larger buffer than this is allowed to use */
101       a = s->toobig;
102   }
103 
104   if(a != s->allc) {
105     /* this logic is not using Curl_saferealloc() to make the tool not have to
106        include that as well when it uses this code */
107     void *p = realloc(s->bufr, a);
108     if(!p) {
109       Curl_dyn_free(s);
110       return CURLE_OUT_OF_MEMORY;
111     }
112     s->bufr = p;
113     s->allc = a;
114   }
115 
116   if(len)
117     memcpy(&s->bufr[indx], mem, len);
118   s->leng = indx + len;
119   s->bufr[s->leng] = 0;
120   return CURLE_OK;
121 }
122 
123 /*
124  * Clears the string, keeps the allocation. This can also be called on a
125  * buffer that already was freed.
126  */
Curl_dyn_reset(struct dynbuf * s)127 void Curl_dyn_reset(struct dynbuf *s)
128 {
129   DEBUGASSERT(s);
130   DEBUGASSERT(s->init == DYNINIT);
131   DEBUGASSERT(!s->leng || s->bufr);
132   if(s->leng)
133     s->bufr[0] = 0;
134   s->leng = 0;
135 }
136 
137 /*
138  * Specify the size of the tail to keep (number of bytes from the end of the
139  * buffer). The rest will be dropped.
140  */
Curl_dyn_tail(struct dynbuf * s,size_t trail)141 CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
142 {
143   DEBUGASSERT(s);
144   DEBUGASSERT(s->init == DYNINIT);
145   DEBUGASSERT(!s->leng || s->bufr);
146   if(trail > s->leng)
147     return CURLE_BAD_FUNCTION_ARGUMENT;
148   else if(trail == s->leng)
149     return CURLE_OK;
150   else if(!trail) {
151     Curl_dyn_reset(s);
152   }
153   else {
154     memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
155     s->leng = trail;
156     s->bufr[s->leng] = 0;
157   }
158   return CURLE_OK;
159 
160 }
161 
162 /*
163  * Appends a buffer with length.
164  */
Curl_dyn_addn(struct dynbuf * s,const void * mem,size_t len)165 CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
166 {
167   DEBUGASSERT(s);
168   DEBUGASSERT(s->init == DYNINIT);
169   DEBUGASSERT(!s->leng || s->bufr);
170   return dyn_nappend(s, mem, len);
171 }
172 
173 /*
174  * Append a null-terminated string at the end.
175  */
Curl_dyn_add(struct dynbuf * s,const char * str)176 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
177 {
178   size_t n;
179   DEBUGASSERT(str);
180   DEBUGASSERT(s);
181   DEBUGASSERT(s->init == DYNINIT);
182   DEBUGASSERT(!s->leng || s->bufr);
183   n = strlen(str);
184   return dyn_nappend(s, (unsigned char *)str, n);
185 }
186 
187 /*
188  * Append a string vprintf()-style
189  */
Curl_dyn_vaddf(struct dynbuf * s,const char * fmt,va_list ap)190 CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
191 {
192 #ifdef BUILDING_LIBCURL
193   int rc;
194   DEBUGASSERT(s);
195   DEBUGASSERT(s->init == DYNINIT);
196   DEBUGASSERT(!s->leng || s->bufr);
197   DEBUGASSERT(fmt);
198   rc = Curl_dyn_vprintf(s, fmt, ap);
199 
200   if(!rc)
201     return CURLE_OK;
202   else if(rc == MERR_TOO_LARGE)
203     return CURLE_TOO_LARGE;
204   return CURLE_OUT_OF_MEMORY;
205 #else
206   char *str;
207   str = vaprintf(fmt, ap); /* this allocs a new string to append */
208 
209   if(str) {
210     CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
211     free(str);
212     return result;
213   }
214   /* If we failed, we cleanup the whole buffer and return error */
215   Curl_dyn_free(s);
216   return CURLE_OK;
217 #endif
218 }
219 
220 /*
221  * Append a string printf()-style
222  */
Curl_dyn_addf(struct dynbuf * s,const char * fmt,...)223 CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
224 {
225   CURLcode result;
226   va_list ap;
227   DEBUGASSERT(s);
228   DEBUGASSERT(s->init == DYNINIT);
229   DEBUGASSERT(!s->leng || s->bufr);
230   va_start(ap, fmt);
231   result = Curl_dyn_vaddf(s, fmt, ap);
232   va_end(ap);
233   return result;
234 }
235 
236 /*
237  * Returns a pointer to the buffer.
238  */
Curl_dyn_ptr(const struct dynbuf * s)239 char *Curl_dyn_ptr(const struct dynbuf *s)
240 {
241   DEBUGASSERT(s);
242   DEBUGASSERT(s->init == DYNINIT);
243   DEBUGASSERT(!s->leng || s->bufr);
244   return s->bufr;
245 }
246 
247 /*
248  * Returns an unsigned pointer to the buffer.
249  */
Curl_dyn_uptr(const struct dynbuf * s)250 unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
251 {
252   DEBUGASSERT(s);
253   DEBUGASSERT(s->init == DYNINIT);
254   DEBUGASSERT(!s->leng || s->bufr);
255   return (unsigned char *)s->bufr;
256 }
257 
258 /*
259  * Returns the length of the buffer.
260  */
Curl_dyn_len(const struct dynbuf * s)261 size_t Curl_dyn_len(const struct dynbuf *s)
262 {
263   DEBUGASSERT(s);
264   DEBUGASSERT(s->init == DYNINIT);
265   DEBUGASSERT(!s->leng || s->bufr);
266   return s->leng;
267 }
268 
269 /*
270  * Set a new (smaller) length.
271  */
Curl_dyn_setlen(struct dynbuf * s,size_t set)272 CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set)
273 {
274   DEBUGASSERT(s);
275   DEBUGASSERT(s->init == DYNINIT);
276   DEBUGASSERT(!s->leng || s->bufr);
277   if(set > s->leng)
278     return CURLE_BAD_FUNCTION_ARGUMENT;
279   s->leng = set;
280   s->bufr[s->leng] = 0;
281   return CURLE_OK;
282 }
283