Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / dynbuf.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2020 - 2022, 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  */
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  */
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  */
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
80   if(fit > s->toobig) {
81     Curl_dyn_free(s);
82     return CURLE_OUT_OF_MEMORY;
83   }
84   else if(!a) {
85     DEBUGASSERT(!indx);
86     /* first invoke */
87     if(fit < MIN_FIRST_ALLOC)
88       a = MIN_FIRST_ALLOC;
89     else
90       a = fit;
91   }
92   else {
93     while(a < fit)
94       a *= 2;
95   }
96
97   if(a != s->allc) {
98     /* this logic is not using Curl_saferealloc() to make the tool not have to
99        include that as well when it uses this code */
100     void *p = realloc(s->bufr, a);
101     if(!p) {
102       Curl_safefree(s->bufr);
103       s->leng = s->allc = 0;
104       return CURLE_OUT_OF_MEMORY;
105     }
106     s->bufr = p;
107     s->allc = a;
108   }
109
110   if(len)
111     memcpy(&s->bufr[indx], mem, len);
112   s->leng = indx + len;
113   s->bufr[s->leng] = 0;
114   return CURLE_OK;
115 }
116
117 /*
118  * Clears the string, keeps the allocation. This can also be called on a
119  * buffer that already was freed.
120  */
121 void Curl_dyn_reset(struct dynbuf *s)
122 {
123   DEBUGASSERT(s);
124   DEBUGASSERT(s->init == DYNINIT);
125   DEBUGASSERT(!s->leng || s->bufr);
126   if(s->leng)
127     s->bufr[0] = 0;
128   s->leng = 0;
129 }
130
131 /*
132  * Specify the size of the tail to keep (number of bytes from the end of the
133  * buffer). The rest will be dropped.
134  */
135 CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
136 {
137   DEBUGASSERT(s);
138   DEBUGASSERT(s->init == DYNINIT);
139   DEBUGASSERT(!s->leng || s->bufr);
140   if(trail > s->leng)
141     return CURLE_BAD_FUNCTION_ARGUMENT;
142   else if(trail == s->leng)
143     return CURLE_OK;
144   else if(!trail) {
145     Curl_dyn_reset(s);
146   }
147   else {
148     memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
149     s->leng = trail;
150     s->bufr[s->leng] = 0;
151   }
152   return CURLE_OK;
153
154 }
155
156 /*
157  * Appends a buffer with length.
158  */
159 CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
160 {
161   DEBUGASSERT(s);
162   DEBUGASSERT(s->init == DYNINIT);
163   DEBUGASSERT(!s->leng || s->bufr);
164   return dyn_nappend(s, mem, len);
165 }
166
167 /*
168  * Append a null-terminated string at the end.
169  */
170 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
171 {
172   size_t n = strlen(str);
173   DEBUGASSERT(s);
174   DEBUGASSERT(s->init == DYNINIT);
175   DEBUGASSERT(!s->leng || s->bufr);
176   return dyn_nappend(s, (unsigned char *)str, n);
177 }
178
179 /*
180  * Append a string vprintf()-style
181  */
182 CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
183 {
184 #ifdef BUILDING_LIBCURL
185   int rc;
186   DEBUGASSERT(s);
187   DEBUGASSERT(s->init == DYNINIT);
188   DEBUGASSERT(!s->leng || s->bufr);
189   rc = Curl_dyn_vprintf(s, fmt, ap);
190
191   if(!rc)
192     return CURLE_OK;
193 #else
194   char *str;
195   str = vaprintf(fmt, ap); /* this allocs a new string to append */
196
197   if(str) {
198     CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
199     free(str);
200     return result;
201   }
202   /* If we failed, we cleanup the whole buffer and return error */
203   Curl_dyn_free(s);
204 #endif
205   return CURLE_OUT_OF_MEMORY;
206 }
207
208 /*
209  * Append a string printf()-style
210  */
211 CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
212 {
213   CURLcode result;
214   va_list ap;
215   DEBUGASSERT(s);
216   DEBUGASSERT(s->init == DYNINIT);
217   DEBUGASSERT(!s->leng || s->bufr);
218   va_start(ap, fmt);
219   result = Curl_dyn_vaddf(s, fmt, ap);
220   va_end(ap);
221   return result;
222 }
223
224 /*
225  * Returns a pointer to the buffer.
226  */
227 char *Curl_dyn_ptr(const struct dynbuf *s)
228 {
229   DEBUGASSERT(s);
230   DEBUGASSERT(s->init == DYNINIT);
231   DEBUGASSERT(!s->leng || s->bufr);
232   return s->bufr;
233 }
234
235 /*
236  * Returns an unsigned pointer to the buffer.
237  */
238 unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
239 {
240   DEBUGASSERT(s);
241   DEBUGASSERT(s->init == DYNINIT);
242   DEBUGASSERT(!s->leng || s->bufr);
243   return (unsigned char *)s->bufr;
244 }
245
246 /*
247  * Returns the length of the buffer.
248  */
249 size_t Curl_dyn_len(const struct dynbuf *s)
250 {
251   DEBUGASSERT(s);
252   DEBUGASSERT(s->init == DYNINIT);
253   DEBUGASSERT(!s->leng || s->bufr);
254   return s->leng;
255 }
256
257 /*
258  * Set a new (smaller) length.
259  */
260 CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set)
261 {
262   DEBUGASSERT(s);
263   DEBUGASSERT(s->init == DYNINIT);
264   DEBUGASSERT(!s->leng || s->bufr);
265   if(set > s->leng)
266     return CURLE_BAD_FUNCTION_ARGUMENT;
267   s->leng = set;
268   s->bufr[s->leng] = 0;
269   return CURLE_OK;
270 }