Coverage Report

Created: 2026-01-10 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/curlx/dynbuf.c
Line
Count
Source
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
#include "../curl_setup.h"
25
26
#include "dynbuf.h"
27
#include "../curl_printf.h"
28
29
5.77k
#define MIN_FIRST_ALLOC 32
30
31
#ifdef DEBUGBUILD
32
2.39k
#define DYNINIT 0xbee51da /* random pattern */
33
#endif
34
35
/*
36
 * Init a dynbuf struct.
37
 */
38
void curlx_dyn_init(struct dynbuf *s, size_t toobig)
39
2.39k
{
40
2.39k
  DEBUGASSERT(s);
41
2.39k
  DEBUGASSERT(toobig);
42
2.39k
  DEBUGASSERT(toobig <= MAX_DYNBUF_SIZE); /* catch crazy mistakes */
43
2.39k
  s->bufr = NULL;
44
2.39k
  s->leng = 0;
45
2.39k
  s->allc = 0;
46
2.39k
  s->toobig = toobig;
47
2.39k
#ifdef DEBUGBUILD
48
2.39k
  s->init = DYNINIT;
49
2.39k
#endif
50
2.39k
}
51
52
/*
53
 * free the buffer and re-init the necessary fields. It does not touch the
54
 * 'init' field and thus this buffer can be reused to add data to again.
55
 */
56
void curlx_dyn_free(struct dynbuf *s)
57
689
{
58
689
  DEBUGASSERT(s);
59
689
  DEBUGASSERT(s->init == DYNINIT);
60
689
  Curl_safefree(s->bufr);
61
689
  s->leng = s->allc = 0;
62
689
}
63
64
/*
65
 * Store/append an chunk of memory to the dynbuf.
66
 */
67
static CURLcode dyn_nappend(struct dynbuf *s,
68
                            const unsigned char *mem, size_t len)
69
18.5M
{
70
18.5M
  size_t idx = s->leng;
71
18.5M
  size_t a = s->allc;
72
18.5M
  size_t fit = len + idx + 1; /* new string + old string + zero byte */
73
74
  /* try to detect if there is rubbish in the struct */
75
18.5M
  DEBUGASSERT(s->init == DYNINIT);
76
18.5M
  DEBUGASSERT(s->toobig);
77
18.5M
  DEBUGASSERT(idx < s->toobig);
78
18.5M
  DEBUGASSERT(!s->leng || s->bufr);
79
18.5M
  DEBUGASSERT(a <= s->toobig);
80
18.5M
  DEBUGASSERT(!len || mem);
81
82
18.5M
  if(fit > s->toobig) {
83
0
    curlx_dyn_free(s);
84
0
    return CURLE_TOO_LARGE;
85
0
  }
86
18.5M
  else if(!a) {
87
2.15k
    DEBUGASSERT(!idx);
88
    /* first invoke */
89
2.15k
    if(MIN_FIRST_ALLOC > s->toobig)
90
226
      a = s->toobig;
91
1.92k
    else if(fit < MIN_FIRST_ALLOC)
92
1.69k
      a = MIN_FIRST_ALLOC;
93
235
    else
94
235
      a = fit;
95
2.15k
  }
96
18.5M
  else {
97
18.5M
    while(a < fit)
98
1.21k
      a *= 2;
99
18.5M
    if(a > s->toobig)
100
      /* no point in allocating a larger buffer than this is allowed to use */
101
118
      a = s->toobig;
102
18.5M
  }
103
104
18.5M
  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
3.37k
    void *p = curlx_realloc(s->bufr, a);
108
3.37k
    if(!p) {
109
0
      curlx_dyn_free(s);
110
0
      return CURLE_OUT_OF_MEMORY;
111
0
    }
112
3.37k
    s->bufr = p;
113
3.37k
    s->allc = a;
114
3.37k
  }
115
116
18.5M
  if(len)
117
18.5M
    memcpy(&s->bufr[idx], mem, len);
118
18.5M
  s->leng = idx + len;
119
18.5M
  s->bufr[s->leng] = 0;
120
18.5M
  return CURLE_OK;
121
18.5M
}
122
123
/*
124
 * Clears the string, keeps the allocation. This can also be called on a
125
 * buffer that already was freed.
126
 */
127
void curlx_dyn_reset(struct dynbuf *s)
128
622
{
129
622
  DEBUGASSERT(s);
130
622
  DEBUGASSERT(s->init == DYNINIT);
131
622
  DEBUGASSERT(!s->leng || s->bufr);
132
622
  if(s->leng)
133
525
    s->bufr[0] = 0;
134
622
  s->leng = 0;
135
622
}
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
 */
141
CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail)
142
0
{
143
0
  DEBUGASSERT(s);
144
0
  DEBUGASSERT(s->init == DYNINIT);
145
0
  DEBUGASSERT(!s->leng || s->bufr);
146
0
  if(trail > s->leng)
147
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
148
0
  else if(trail == s->leng)
149
0
    return CURLE_OK;
150
0
  else if(!trail) {
151
0
    curlx_dyn_reset(s);
152
0
  }
153
0
  else {
154
0
    memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
155
0
    s->leng = trail;
156
0
    s->bufr[s->leng] = 0;
157
0
  }
158
0
  return CURLE_OK;
159
0
}
160
161
/*
162
 * Appends a buffer with length.
163
 */
164
CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
165
18.5M
{
166
18.5M
  DEBUGASSERT(s);
167
18.5M
  DEBUGASSERT(s->init == DYNINIT);
168
18.5M
  DEBUGASSERT(!s->leng || s->bufr);
169
18.5M
  return dyn_nappend(s, mem, len);
170
18.5M
}
171
172
/*
173
 * Append a null-terminated string at the end.
174
 */
175
CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
176
0
{
177
0
  size_t n;
178
0
  DEBUGASSERT(str);
179
0
  DEBUGASSERT(s);
180
0
  DEBUGASSERT(s->init == DYNINIT);
181
0
  DEBUGASSERT(!s->leng || s->bufr);
182
0
  n = strlen(str);
183
0
  return dyn_nappend(s, (const unsigned char *)str, n);
184
0
}
185
186
/*
187
 * Append a string vprintf()-style
188
 */
189
CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
190
275
{
191
275
#ifdef BUILDING_LIBCURL
192
275
  int rc;
193
275
  DEBUGASSERT(s);
194
275
  DEBUGASSERT(s->init == DYNINIT);
195
275
  DEBUGASSERT(!s->leng || s->bufr);
196
275
  DEBUGASSERT(fmt);
197
275
  rc = curlx_dyn_vprintf(s, fmt, ap);
198
199
275
  if(!rc)
200
275
    return CURLE_OK;
201
0
  else if(rc == MERR_TOO_LARGE)
202
0
    return CURLE_TOO_LARGE;
203
0
  return CURLE_OUT_OF_MEMORY;
204
#else
205
  char *str;
206
  str = curl_mvaprintf(fmt, ap); /* this allocs a new string to append */
207
208
  if(str) {
209
    CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str));
210
    curlx_free(str);
211
    return result;
212
  }
213
  /* If we failed, we cleanup the whole buffer and return error */
214
  curlx_dyn_free(s);
215
  return CURLE_OUT_OF_MEMORY;
216
#endif
217
275
}
218
219
/*
220
 * Append a string printf()-style
221
 */
222
CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
223
275
{
224
275
  CURLcode result;
225
275
  va_list ap;
226
275
  DEBUGASSERT(s);
227
275
  DEBUGASSERT(s->init == DYNINIT);
228
275
  DEBUGASSERT(!s->leng || s->bufr);
229
275
  DEBUGASSERT(strcmp(fmt, "%s")); /* use curlx_dyn_add instead */
230
275
  va_start(ap, fmt);
231
275
  result = curlx_dyn_vaddf(s, fmt, ap);
232
275
  va_end(ap);
233
275
  return result;
234
275
}
235
236
/*
237
 * Returns a pointer to the buffer.
238
 */
239
char *curlx_dyn_ptr(const struct dynbuf *s)
240
17.6k
{
241
17.6k
  DEBUGASSERT(s);
242
17.6k
  DEBUGASSERT(s->init == DYNINIT);
243
17.6k
  DEBUGASSERT(!s->leng || s->bufr);
244
17.6k
  return s->bufr;
245
17.6k
}
246
247
char *curlx_dyn_take(struct dynbuf *s, size_t *plen)
248
0
{
249
0
  char *ptr = s->bufr;
250
0
  DEBUGASSERT(s);
251
0
  DEBUGASSERT(s->init == DYNINIT);
252
0
  *plen = s->leng;
253
0
  s->bufr = NULL;
254
0
  s->leng = 0;
255
0
  s->allc = 0;
256
0
  return ptr;
257
0
}
258
259
/*
260
 * Returns an unsigned pointer to the buffer.
261
 */
262
unsigned char *curlx_dyn_uptr(const struct dynbuf *s)
263
0
{
264
0
  DEBUGASSERT(s);
265
0
  DEBUGASSERT(s->init == DYNINIT);
266
0
  DEBUGASSERT(!s->leng || s->bufr);
267
0
  return (unsigned char *)s->bufr;
268
0
}
269
270
/*
271
 * Returns the length of the buffer.
272
 */
273
size_t curlx_dyn_len(const struct dynbuf *s)
274
18.8k
{
275
18.8k
  DEBUGASSERT(s);
276
18.8k
  DEBUGASSERT(s->init == DYNINIT);
277
18.8k
  DEBUGASSERT(!s->leng || s->bufr);
278
18.8k
  return s->leng;
279
18.8k
}
280
281
/*
282
 * Set a new (smaller) length.
283
 */
284
CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set)
285
9.39k
{
286
9.39k
  DEBUGASSERT(s);
287
9.39k
  DEBUGASSERT(s->init == DYNINIT);
288
9.39k
  DEBUGASSERT(!s->leng || s->bufr);
289
9.39k
  if(set > s->leng)
290
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
291
9.39k
  s->leng = set;
292
9.39k
  s->bufr[s->leng] = 0;
293
9.39k
  return CURLE_OK;
294
9.39k
}