Coverage Report

Created: 2025-12-05 06:38

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
25
#include "../curl_setup.h"
26
#include "dynbuf.h"
27
#include "../curl_printf.h"
28
29
23.1M
#define MIN_FIRST_ALLOC 32
30
31
#ifdef DEBUGBUILD
32
6.96M
#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
6.96M
{
40
6.96M
  DEBUGASSERT(s);
41
6.96M
  DEBUGASSERT(toobig);
42
6.96M
  DEBUGASSERT(toobig <= MAX_DYNBUF_SIZE); /* catch crazy mistakes */
43
6.96M
  s->bufr = NULL;
44
6.96M
  s->leng = 0;
45
6.96M
  s->allc = 0;
46
6.96M
  s->toobig = toobig;
47
6.96M
#ifdef DEBUGBUILD
48
6.96M
  s->init = DYNINIT;
49
6.96M
#endif
50
6.96M
}
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
3.50M
{
58
3.50M
  DEBUGASSERT(s);
59
3.50M
  DEBUGASSERT(s->init == DYNINIT);
60
3.50M
  Curl_safefree(s->bufr);
61
3.50M
  s->leng = s->allc = 0;
62
3.50M
}
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
798M
{
70
798M
  size_t idx = s->leng;
71
798M
  size_t a = s->allc;
72
798M
  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
798M
  DEBUGASSERT(s->init == DYNINIT);
76
798M
  DEBUGASSERT(s->toobig);
77
798M
  DEBUGASSERT(idx < s->toobig);
78
798M
  DEBUGASSERT(!s->leng || s->bufr);
79
798M
  DEBUGASSERT(a <= s->toobig);
80
798M
  DEBUGASSERT(!len || mem);
81
82
798M
  if(fit > s->toobig) {
83
283
    curlx_dyn_free(s);
84
283
    return CURLE_TOO_LARGE;
85
283
  }
86
798M
  else if(!a) {
87
7.84M
    DEBUGASSERT(!idx);
88
    /* first invoke */
89
7.84M
    if(MIN_FIRST_ALLOC > s->toobig)
90
166k
      a = s->toobig;
91
7.67M
    else if(fit < MIN_FIRST_ALLOC)
92
7.61M
      a = MIN_FIRST_ALLOC;
93
61.2k
    else
94
61.2k
      a = fit;
95
7.84M
  }
96
791M
  else {
97
792M
    while(a < fit)
98
1.59M
      a *= 2;
99
791M
    if(a > s->toobig)
100
      /* no point in allocating a larger buffer than this is allowed to use */
101
19.5k
      a = s->toobig;
102
791M
  }
103
104
798M
  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
9.40M
    void *p = curlx_realloc(s->bufr, a);
108
9.40M
    if(!p) {
109
0
      curlx_dyn_free(s);
110
0
      return CURLE_OUT_OF_MEMORY;
111
0
    }
112
9.40M
    s->bufr = p;
113
9.40M
    s->allc = a;
114
9.40M
  }
115
116
798M
  if(len)
117
793M
    memcpy(&s->bufr[idx], mem, len);
118
798M
  s->leng = idx + len;
119
798M
  s->bufr[s->leng] = 0;
120
798M
  return CURLE_OK;
121
798M
}
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
2.41M
{
129
2.41M
  DEBUGASSERT(s);
130
2.41M
  DEBUGASSERT(s->init == DYNINIT);
131
2.41M
  DEBUGASSERT(!s->leng || s->bufr);
132
2.41M
  if(s->leng)
133
805k
    s->bufr[0] = 0;
134
2.41M
  s->leng = 0;
135
2.41M
}
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
2.71M
{
143
2.71M
  DEBUGASSERT(s);
144
2.71M
  DEBUGASSERT(s->init == DYNINIT);
145
2.71M
  DEBUGASSERT(!s->leng || s->bufr);
146
2.71M
  if(trail > s->leng)
147
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
148
2.71M
  else if(trail == s->leng)
149
2
    return CURLE_OK;
150
2.71M
  else if(!trail) {
151
3.92k
    curlx_dyn_reset(s);
152
3.92k
  }
153
2.70M
  else {
154
2.70M
    memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
155
2.70M
    s->leng = trail;
156
2.70M
    s->bufr[s->leng] = 0;
157
2.70M
  }
158
2.71M
  return CURLE_OK;
159
2.71M
}
160
161
/*
162
 * Appends a buffer with length.
163
 */
164
CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
165
798M
{
166
798M
  DEBUGASSERT(s);
167
798M
  DEBUGASSERT(s->init == DYNINIT);
168
798M
  DEBUGASSERT(!s->leng || s->bufr);
169
798M
  return dyn_nappend(s, mem, len);
170
798M
}
171
172
/*
173
 * Append a null-terminated string at the end.
174
 */
175
CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
176
413k
{
177
413k
  size_t n;
178
413k
  DEBUGASSERT(str);
179
413k
  DEBUGASSERT(s);
180
413k
  DEBUGASSERT(s->init == DYNINIT);
181
413k
  DEBUGASSERT(!s->leng || s->bufr);
182
413k
  n = strlen(str);
183
413k
  return dyn_nappend(s, (const unsigned char *)str, n);
184
413k
}
185
186
/*
187
 * Append a string vprintf()-style
188
 */
189
CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
190
3.15M
{
191
3.15M
#ifdef BUILDING_LIBCURL
192
3.15M
  int rc;
193
3.15M
  DEBUGASSERT(s);
194
3.15M
  DEBUGASSERT(s->init == DYNINIT);
195
3.15M
  DEBUGASSERT(!s->leng || s->bufr);
196
3.15M
  DEBUGASSERT(fmt);
197
3.15M
  rc = curlx_dyn_vprintf(s, fmt, ap);
198
199
3.15M
  if(!rc)
200
3.15M
    return CURLE_OK;
201
183
  else if(rc == MERR_TOO_LARGE)
202
183
    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
3.15M
}
218
219
/*
220
 * Append a string printf()-style
221
 */
222
CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
223
3.13M
{
224
3.13M
  CURLcode result;
225
3.13M
  va_list ap;
226
3.13M
  DEBUGASSERT(s);
227
3.13M
  DEBUGASSERT(s->init == DYNINIT);
228
3.13M
  DEBUGASSERT(!s->leng || s->bufr);
229
3.13M
  DEBUGASSERT(strcmp(fmt, "%s")); /* use curlx_dyn_add instead */
230
3.13M
  va_start(ap, fmt);
231
3.13M
  result = curlx_dyn_vaddf(s, fmt, ap);
232
3.13M
  va_end(ap);
233
3.13M
  return result;
234
3.13M
}
235
236
/*
237
 * Returns a pointer to the buffer.
238
 */
239
char *curlx_dyn_ptr(const struct dynbuf *s)
240
12.5M
{
241
12.5M
  DEBUGASSERT(s);
242
12.5M
  DEBUGASSERT(s->init == DYNINIT);
243
12.5M
  DEBUGASSERT(!s->leng || s->bufr);
244
12.5M
  return s->bufr;
245
12.5M
}
246
247
char *curlx_dyn_take(struct dynbuf *s, size_t *plen)
248
31.4k
{
249
31.4k
  char *ptr = s->bufr;
250
31.4k
  DEBUGASSERT(s);
251
31.4k
  DEBUGASSERT(s->init == DYNINIT);
252
31.4k
  *plen = s->leng;
253
31.4k
  s->bufr = NULL;
254
31.4k
  s->leng = 0;
255
31.4k
  s->allc = 0;
256
31.4k
  return ptr;
257
31.4k
}
258
259
/*
260
 * Returns an unsigned pointer to the buffer.
261
 */
262
unsigned char *curlx_dyn_uptr(const struct dynbuf *s)
263
22.5k
{
264
22.5k
  DEBUGASSERT(s);
265
22.5k
  DEBUGASSERT(s->init == DYNINIT);
266
22.5k
  DEBUGASSERT(!s->leng || s->bufr);
267
22.5k
  return (unsigned char *)s->bufr;
268
22.5k
}
269
270
/*
271
 * Returns the length of the buffer.
272
 */
273
size_t curlx_dyn_len(const struct dynbuf *s)
274
35.3M
{
275
35.3M
  DEBUGASSERT(s);
276
35.3M
  DEBUGASSERT(s->init == DYNINIT);
277
35.3M
  DEBUGASSERT(!s->leng || s->bufr);
278
35.3M
  return s->leng;
279
35.3M
}
280
281
/*
282
 * Set a new (smaller) length.
283
 */
284
CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set)
285
122k
{
286
122k
  DEBUGASSERT(s);
287
122k
  DEBUGASSERT(s->init == DYNINIT);
288
122k
  DEBUGASSERT(!s->leng || s->bufr);
289
122k
  if(set > s->leng)
290
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
291
122k
  s->leng = set;
292
122k
  s->bufr[s->leng] = 0;
293
122k
  return CURLE_OK;
294
122k
}