xref: /third_party/curl/lib/dynbuf.c (revision 13498266)
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 */
40void 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 */
57void 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 */
67static 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 */
127void 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 */
141CURLcode 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 */
165CURLcode 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 */
176CURLcode 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 */
190CURLcode 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 */
223CURLcode 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 */
239char *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 */
250unsigned 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 */
261size_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 */
272CURLcode 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