Line | Count | Source |
1 | | /* Copyright (C) 2017 the mpv developers |
2 | | * |
3 | | * Permission to use, copy, modify, and/or distribute this software for any |
4 | | * purpose with or without fee is hereby granted, provided that the above |
5 | | * copyright notice and this permission notice appear in all copies. |
6 | | * |
7 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
8 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
10 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
11 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
12 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
13 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | | */ |
15 | | |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | #include <stdio.h> |
19 | | #include <assert.h> |
20 | | #include "osdep/strnlen.h" |
21 | | |
22 | | #define TA_NO_WRAPPERS |
23 | | #include "ta.h" |
24 | | |
25 | | // Return element_size * count. If it overflows, return (size_t)-1 (SIZE_MAX). |
26 | | // I.e. this returns the equivalent of: MIN(element_size * count, SIZE_MAX). |
27 | | // The idea is that every real memory allocator will reject (size_t)-1, thus |
28 | | // this is a valid way to handle too large array allocation requests. |
29 | | size_t ta_calc_array_size(size_t element_size, size_t count) |
30 | 88.7M | { |
31 | 88.7M | if (count > (((size_t)-1) / element_size)) |
32 | 0 | return (size_t)-1; |
33 | 88.7M | return element_size * count; |
34 | 88.7M | } |
35 | | |
36 | | // This is used when an array has to be enlarged for appending new elements. |
37 | | // Return a "good" size for the new array (in number of elements). This returns |
38 | | // a value > nextidx, unless the calculation overflows, in which case SIZE_MAX |
39 | | // is returned. |
40 | | size_t ta_calc_prealloc_elems(size_t nextidx) |
41 | 58.4M | { |
42 | 58.4M | if (nextidx >= ((size_t)-1) / 2 - 1) |
43 | 0 | return (size_t)-1; |
44 | 58.4M | return (nextidx + 1) * 2; |
45 | 58.4M | } |
46 | | |
47 | | /* Create an empty (size 0) TA allocation. |
48 | | */ |
49 | | void *ta_new_context(void *ta_parent) |
50 | 24.8M | { |
51 | 24.8M | return ta_alloc_size(ta_parent, 0); |
52 | 24.8M | } |
53 | | |
54 | | /* Set parent of ptr to ta_parent, return the ptr. |
55 | | * Note that ta_parent==NULL will simply unset the current parent of ptr. |
56 | | */ |
57 | | void *ta_steal_(void *ta_parent, void *ptr) |
58 | 10.6M | { |
59 | 10.6M | ta_set_parent(ptr, ta_parent); |
60 | 10.6M | return ptr; |
61 | 10.6M | } |
62 | | |
63 | | /* Duplicate the memory at ptr with the given size. |
64 | | */ |
65 | | void *ta_memdup(void *ta_parent, void *ptr, size_t size) |
66 | 7.53M | { |
67 | 7.53M | if (!ptr) { |
68 | 2.45M | assert(!size); |
69 | 2.45M | return NULL; |
70 | 2.45M | } |
71 | 5.08M | void *res = ta_alloc_size(ta_parent, size); |
72 | 5.08M | if (!res) |
73 | 0 | return NULL; |
74 | 5.08M | memcpy(res, ptr, size); |
75 | 5.08M | return res; |
76 | 5.08M | } |
77 | | |
78 | | // *str = *str[0..at] + append[0..append_len] |
79 | | // (append_len being a maximum length; shorter if embedded \0s are encountered) |
80 | | static bool strndup_append_at(char **str, size_t at, const char *append, |
81 | | size_t append_len) |
82 | 712M | { |
83 | 712M | assert(ta_get_size(*str) >= at); |
84 | | |
85 | 712M | if (!*str && !append) |
86 | 0 | return true; // stays NULL, but not an OOM condition |
87 | | |
88 | 712M | size_t real_len = append ? strnlen(append, append_len) : 0; |
89 | 712M | if (append_len > real_len) |
90 | 2.08M | append_len = real_len; |
91 | | |
92 | 712M | if (ta_get_size(*str) < at + append_len + 1) { |
93 | 712M | char *t = ta_realloc_size(NULL, *str, at + append_len + 1); |
94 | 712M | if (!t) |
95 | 0 | return false; |
96 | 712M | *str = t; |
97 | 712M | } |
98 | | |
99 | 712M | if (append_len) |
100 | 706M | memcpy(*str + at, append, append_len); |
101 | | |
102 | 712M | (*str)[at + append_len] = '\0'; |
103 | | |
104 | 712M | ta_dbg_mark_as_string(*str); |
105 | | |
106 | 712M | return true; |
107 | 712M | } |
108 | | |
109 | | /* Return a copy of str. |
110 | | * Returns NULL on OOM. |
111 | | */ |
112 | | char *ta_strdup(void *ta_parent, const char *str) |
113 | 572M | { |
114 | 572M | return ta_strndup(ta_parent, str, str ? strlen(str) : 0); |
115 | 572M | } |
116 | | |
117 | | /* Return a copy of str. If the string is longer than n, copy only n characters |
118 | | * (the returned allocation will be n+1 bytes and contain a terminating '\0'). |
119 | | * The returned string will have the length MIN(strlen(str), n) |
120 | | * If str==NULL, return NULL. Returns NULL on OOM as well. |
121 | | */ |
122 | | char *ta_strndup(void *ta_parent, const char *str, size_t n) |
123 | 780M | { |
124 | 780M | if (!str) |
125 | 71.2M | return NULL; |
126 | 709M | char *new = NULL; |
127 | 709M | strndup_append_at(&new, 0, str, n); |
128 | 709M | ta_set_parent(new, ta_parent); |
129 | 709M | return new; |
130 | 780M | } |
131 | | |
132 | | /* Append a to *str. If *str is NULL, the string is newly allocated, otherwise |
133 | | * ta_realloc() is used on *str as needed. |
134 | | * Return success or failure (it can fail due to OOM only). |
135 | | */ |
136 | | bool ta_strdup_append(char **str, const char *a) |
137 | 377k | { |
138 | 377k | return strndup_append_at(str, *str ? strlen(*str) : 0, a, (size_t)-1); |
139 | 377k | } |
140 | | |
141 | | /* Like ta_strdup_append(), but use ta_get_size(*str)-1 instead of strlen(*str). |
142 | | * (See also: ta_asprintf_append_buffer()) |
143 | | */ |
144 | | bool ta_strdup_append_buffer(char **str, const char *a) |
145 | 1.64M | { |
146 | 1.64M | size_t size = ta_get_size(*str); |
147 | 1.64M | if (size > 0) |
148 | 1.64M | size -= 1; |
149 | 1.64M | return strndup_append_at(str, size, a, (size_t)-1); |
150 | 1.64M | } |
151 | | |
152 | | /* Like ta_strdup_append(), but limit the length of a with n. |
153 | | * (See also: ta_strndup()) |
154 | | */ |
155 | | bool ta_strndup_append(char **str, const char *a, size_t n) |
156 | 0 | { |
157 | 0 | return strndup_append_at(str, *str ? strlen(*str) : 0, a, n); |
158 | 0 | } |
159 | | |
160 | | /* Like ta_strdup_append_buffer(), but limit the length of a with n. |
161 | | * (See also: ta_strndup()) |
162 | | */ |
163 | | bool ta_strndup_append_buffer(char **str, const char *a, size_t n) |
164 | 1.63M | { |
165 | 1.63M | size_t size = ta_get_size(*str); |
166 | 1.63M | if (size > 0) |
167 | 1.63M | size -= 1; |
168 | 1.63M | return strndup_append_at(str, size, a, n); |
169 | 1.63M | } |
170 | | |
171 | | TA_PRF(3, 0) |
172 | | static bool ta_vasprintf_append_at(char **str, size_t at, const char *fmt, |
173 | | va_list ap) |
174 | 27.3M | { |
175 | 27.3M | assert(ta_get_size(*str) >= at); |
176 | | |
177 | 27.3M | int size; |
178 | 27.3M | va_list copy; |
179 | 27.3M | va_copy(copy, ap); |
180 | 27.3M | char c; |
181 | 27.3M | size = vsnprintf(&c, 1, fmt, copy); |
182 | 27.3M | va_end(copy); |
183 | | |
184 | 27.3M | if (size < 0) |
185 | 0 | return false; |
186 | | |
187 | 27.3M | if (ta_get_size(*str) < at + size + 1) { |
188 | 27.3M | char *t = ta_realloc_size(NULL, *str, at + size + 1); |
189 | 27.3M | if (!t) |
190 | 0 | return false; |
191 | 27.3M | *str = t; |
192 | 27.3M | } |
193 | 27.3M | vsnprintf(*str + at, size + 1, fmt, ap); |
194 | | |
195 | 27.3M | ta_dbg_mark_as_string(*str); |
196 | | |
197 | 27.3M | return true; |
198 | 27.3M | } |
199 | | |
200 | | /* Like snprintf(); returns the formatted string as allocation (or NULL on OOM |
201 | | * or snprintf() errors). |
202 | | */ |
203 | | char *ta_asprintf(void *ta_parent, const char *fmt, ...) |
204 | 25.7M | { |
205 | 25.7M | char *res; |
206 | 25.7M | va_list ap; |
207 | 25.7M | va_start(ap, fmt); |
208 | 25.7M | res = ta_vasprintf(ta_parent, fmt, ap); |
209 | 25.7M | va_end(ap); |
210 | 25.7M | return res; |
211 | 25.7M | } |
212 | | |
213 | | char *ta_vasprintf(void *ta_parent, const char *fmt, va_list ap) |
214 | 25.7M | { |
215 | 25.7M | char *res = NULL; |
216 | 25.7M | ta_vasprintf_append_at(&res, 0, fmt, ap); |
217 | 25.7M | ta_set_parent(res, ta_parent); |
218 | 25.7M | if (!res) { |
219 | 0 | ta_free(res); |
220 | 0 | return NULL; |
221 | 0 | } |
222 | 25.7M | return res; |
223 | 25.7M | } |
224 | | |
225 | | /* Append the formatted string to *str (after strlen(*str)). The allocation is |
226 | | * ta_realloced if needed. |
227 | | * Returns false on OOM or snprintf() errors, with *str left untouched. |
228 | | */ |
229 | | bool ta_asprintf_append(char **str, const char *fmt, ...) |
230 | 1.51M | { |
231 | 1.51M | bool res; |
232 | 1.51M | va_list ap; |
233 | 1.51M | va_start(ap, fmt); |
234 | 1.51M | res = ta_vasprintf_append(str, fmt, ap); |
235 | 1.51M | va_end(ap); |
236 | 1.51M | return res; |
237 | 1.51M | } |
238 | | |
239 | | bool ta_vasprintf_append(char **str, const char *fmt, va_list ap) |
240 | 1.51M | { |
241 | 1.51M | return ta_vasprintf_append_at(str, *str ? strlen(*str) : 0, fmt, ap); |
242 | 1.51M | } |
243 | | |
244 | | /* Append the formatted string at the end of the allocation of *str. It |
245 | | * overwrites the last byte of the allocation too (which is assumed to be the |
246 | | * '\0' terminating the string). Compared to ta_asprintf_append(), this is |
247 | | * useful if you know that the string ends with the allocation, so that the |
248 | | * extra strlen() can be avoided for better performance. |
249 | | * Returns false on OOM or snprintf() errors, with *str left untouched. |
250 | | */ |
251 | | bool ta_asprintf_append_buffer(char **str, const char *fmt, ...) |
252 | 0 | { |
253 | 0 | bool res; |
254 | 0 | va_list ap; |
255 | 0 | va_start(ap, fmt); |
256 | 0 | res = ta_vasprintf_append_buffer(str, fmt, ap); |
257 | 0 | va_end(ap); |
258 | 0 | return res; |
259 | 0 | } |
260 | | |
261 | | bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) |
262 | 97.3k | { |
263 | 97.3k | size_t size = ta_get_size(*str); |
264 | 97.3k | if (size > 0) |
265 | 97.3k | size -= 1; |
266 | 97.3k | return ta_vasprintf_append_at(str, size, fmt, ap); |
267 | 97.3k | } |
268 | | |
269 | | void *ta_xmemdup(void *ta_parent, void *ptr, size_t size) |
270 | 5.96M | { |
271 | 5.96M | void *new = ta_memdup(ta_parent, ptr, size); |
272 | 5.96M | ta_oom_b(new || !ptr); |
273 | 5.96M | return new; |
274 | 5.96M | } |
275 | | |
276 | | void *ta_xrealloc_size(void *ta_parent, void *ptr, size_t size) |
277 | 103M | { |
278 | 103M | ptr = ta_realloc_size(ta_parent, ptr, size); |
279 | 103M | ta_oom_b(ptr || !size); |
280 | 103M | return ptr; |
281 | 103M | } |
282 | | |
283 | | char *ta_xstrdup(void *ta_parent, const char *str) |
284 | 572M | { |
285 | 572M | char *res = ta_strdup(ta_parent, str); |
286 | 572M | ta_oom_b(res || !str); |
287 | 572M | return res; |
288 | 572M | } |
289 | | |
290 | | char *ta_xstrndup(void *ta_parent, const char *str, size_t n) |
291 | 208M | { |
292 | 208M | char *res = ta_strndup(ta_parent, str, n); |
293 | 208M | ta_oom_b(res || !str); |
294 | 208M | return res; |
295 | 208M | } |