aboutsummaryrefslogtreecommitdiffstats
path: root/lib/malloc
diff options
context:
space:
mode:
authorPaul Eggert2021-01-22 11:45:38 -0800
committerPaul Eggert2021-01-22 12:02:55 -0800
commita900e641fa1fd765799f12a7f699f768ebfccfe8 (patch)
treea874eba952d3e51190a2bfe26cfe6c1635e9fda1 /lib/malloc
parent9143eba0c6861f467c18bc52d66e6f5c573be56b (diff)
downloademacs-a900e641fa1fd765799f12a7f699f768ebfccfe8.tar.gz
emacs-a900e641fa1fd765799f12a7f699f768ebfccfe8.zip
Update from Gnulib by running admin/merge-gnulib
Diffstat (limited to 'lib/malloc')
-rw-r--r--lib/malloc/dynarray-skeleton.c525
-rw-r--r--lib/malloc/dynarray.h178
-rw-r--r--lib/malloc/dynarray_at_failure.c35
-rw-r--r--lib/malloc/dynarray_emplace_enlarge.c73
-rw-r--r--lib/malloc/dynarray_finalize.c62
-rw-r--r--lib/malloc/dynarray_resize.c64
-rw-r--r--lib/malloc/dynarray_resize_clear.c35
-rw-r--r--lib/malloc/scratch_buffer_grow.c2
-rw-r--r--lib/malloc/scratch_buffer_grow_preserve.c2
-rw-r--r--lib/malloc/scratch_buffer_set_array_size.c2
10 files changed, 975 insertions, 3 deletions
diff --git a/lib/malloc/dynarray-skeleton.c b/lib/malloc/dynarray-skeleton.c
new file mode 100644
index 00000000000..4995fd1c049
--- /dev/null
+++ b/lib/malloc/dynarray-skeleton.c
@@ -0,0 +1,525 @@
1/* Type-safe arrays which grow dynamically.
2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* Pre-processor macros which act as parameters:
20
21 DYNARRAY_STRUCT
22 The struct tag of dynamic array to be defined.
23 DYNARRAY_ELEMENT
24 The type name of the element type. Elements are copied
25 as if by memcpy, and can change address as the dynamic
26 array grows.
27 DYNARRAY_PREFIX
28 The prefix of the functions which are defined.
29
30 The following parameters are optional:
31
32 DYNARRAY_ELEMENT_FREE
33 DYNARRAY_ELEMENT_FREE (E) is evaluated to deallocate the
34 contents of elements. E is of type DYNARRAY_ELEMENT *.
35 DYNARRAY_ELEMENT_INIT
36 DYNARRAY_ELEMENT_INIT (E) is evaluated to initialize a new
37 element. E is of type DYNARRAY_ELEMENT *.
38 If DYNARRAY_ELEMENT_FREE but not DYNARRAY_ELEMENT_INIT is
39 defined, new elements are automatically zero-initialized.
40 Otherwise, new elements have undefined contents.
41 DYNARRAY_INITIAL_SIZE
42 The size of the statically allocated array (default:
43 at least 2, more elements if they fit into 128 bytes).
44 Must be a preprocessor constant. If DYNARRAY_INITIAL_SIZE is 0,
45 there is no statically allocated array at, and all non-empty
46 arrays are heap-allocated.
47 DYNARRAY_FINAL_TYPE
48 The name of the type which holds the final array. If not
49 defined, is PREFIX##finalize not provided. DYNARRAY_FINAL_TYPE
50 must be a struct type, with members of type DYNARRAY_ELEMENT and
51 size_t at the start (in this order).
52
53 These macros are undefined after this header file has been
54 included.
55
56 The following types are provided (their members are private to the
57 dynarray implementation):
58
59 struct DYNARRAY_STRUCT
60
61 The following functions are provided:
62
63 void DYNARRAY_PREFIX##init (struct DYNARRAY_STRUCT *);
64 void DYNARRAY_PREFIX##free (struct DYNARRAY_STRUCT *);
65 bool DYNARRAY_PREFIX##has_failed (const struct DYNARRAY_STRUCT *);
66 void DYNARRAY_PREFIX##mark_failed (struct DYNARRAY_STRUCT *);
67 size_t DYNARRAY_PREFIX##size (const struct DYNARRAY_STRUCT *);
68 DYNARRAY_ELEMENT *DYNARRAY_PREFIX##begin (const struct DYNARRAY_STRUCT *);
69 DYNARRAY_ELEMENT *DYNARRAY_PREFIX##end (const struct DYNARRAY_STRUCT *);
70 DYNARRAY_ELEMENT *DYNARRAY_PREFIX##at (struct DYNARRAY_STRUCT *, size_t);
71 void DYNARRAY_PREFIX##add (struct DYNARRAY_STRUCT *, DYNARRAY_ELEMENT);
72 DYNARRAY_ELEMENT *DYNARRAY_PREFIX##emplace (struct DYNARRAY_STRUCT *);
73 bool DYNARRAY_PREFIX##resize (struct DYNARRAY_STRUCT *, size_t);
74 void DYNARRAY_PREFIX##remove_last (struct DYNARRAY_STRUCT *);
75 void DYNARRAY_PREFIX##clear (struct DYNARRAY_STRUCT *);
76
77 The following functions are provided are provided if the
78 prerequisites are met:
79
80 bool DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
81 DYNARRAY_FINAL_TYPE *);
82 (if DYNARRAY_FINAL_TYPE is defined)
83 DYNARRAY_ELEMENT *DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
84 size_t *);
85 (if DYNARRAY_FINAL_TYPE is not defined)
86*/
87
88#include <malloc/dynarray.h>
89
90#include <errno.h>
91#include <stdlib.h>
92#include <string.h>
93
94#ifndef DYNARRAY_STRUCT
95# error "DYNARRAY_STRUCT must be defined"
96#endif
97
98#ifndef DYNARRAY_ELEMENT
99# error "DYNARRAY_ELEMENT must be defined"
100#endif
101
102#ifndef DYNARRAY_PREFIX
103# error "DYNARRAY_PREFIX must be defined"
104#endif
105
106#ifdef DYNARRAY_INITIAL_SIZE
107# if DYNARRAY_INITIAL_SIZE < 0
108# error "DYNARRAY_INITIAL_SIZE must be non-negative"
109# endif
110# if DYNARRAY_INITIAL_SIZE > 0
111# define DYNARRAY_HAVE_SCRATCH 1
112# else
113# define DYNARRAY_HAVE_SCRATCH 0
114# endif
115#else
116/* Provide a reasonable default which limits the size of
117 DYNARRAY_STRUCT. */
118# define DYNARRAY_INITIAL_SIZE \
119 (sizeof (DYNARRAY_ELEMENT) > 64 ? 2 : 128 / sizeof (DYNARRAY_ELEMENT))
120# define DYNARRAY_HAVE_SCRATCH 1
121#endif
122
123/* Public type definitions. */
124
125/* All fields of this struct are private to the implementation. */
126struct DYNARRAY_STRUCT
127{
128 union
129 {
130 struct dynarray_header dynarray_abstract;
131 struct
132 {
133 /* These fields must match struct dynarray_header. */
134 size_t used;
135 size_t allocated;
136 DYNARRAY_ELEMENT *array;
137 } dynarray_header;
138 } u;
139
140#if DYNARRAY_HAVE_SCRATCH
141 /* Initial inline allocation. */
142 DYNARRAY_ELEMENT scratch[DYNARRAY_INITIAL_SIZE];
143#endif
144};
145
146/* Internal use only: Helper macros. */
147
148/* Ensure macro-expansion of DYNARRAY_PREFIX. */
149#define DYNARRAY_CONCAT0(prefix, name) prefix##name
150#define DYNARRAY_CONCAT1(prefix, name) DYNARRAY_CONCAT0(prefix, name)
151#define DYNARRAY_NAME(name) DYNARRAY_CONCAT1(DYNARRAY_PREFIX, name)
152
153/* Use DYNARRAY_FREE instead of DYNARRAY_NAME (free),
154 so that Gnulib does not change 'free' to 'rpl_free'. */
155#define DYNARRAY_FREE DYNARRAY_CONCAT1 (DYNARRAY_NAME (f), ree)
156
157/* Address of the scratch buffer if any. */
158#if DYNARRAY_HAVE_SCRATCH
159# define DYNARRAY_SCRATCH(list) (list)->scratch
160#else
161# define DYNARRAY_SCRATCH(list) NULL
162#endif
163
164/* Internal use only: Helper functions. */
165
166/* Internal function. Call DYNARRAY_ELEMENT_FREE with the array
167 elements. Name mangling needed due to the DYNARRAY_ELEMENT_FREE
168 macro expansion. */
169static inline void
170DYNARRAY_NAME (free__elements__) (DYNARRAY_ELEMENT *__dynarray_array,
171 size_t __dynarray_used)
172{
173#ifdef DYNARRAY_ELEMENT_FREE
174 for (size_t __dynarray_i = 0; __dynarray_i < __dynarray_used; ++__dynarray_i)
175 DYNARRAY_ELEMENT_FREE (&__dynarray_array[__dynarray_i]);
176#endif /* DYNARRAY_ELEMENT_FREE */
177}
178
179/* Internal function. Free the non-scratch array allocation. */
180static inline void
181DYNARRAY_NAME (free__array__) (struct DYNARRAY_STRUCT *list)
182{
183#if DYNARRAY_HAVE_SCRATCH
184 if (list->u.dynarray_header.array != list->scratch)
185 free (list->u.dynarray_header.array);
186#else
187 free (list->u.dynarray_header.array);
188#endif
189}
190
191/* Public functions. */
192
193/* Initialize a dynamic array object. This must be called before any
194 use of the object. */
195__nonnull ((1))
196static void
197DYNARRAY_NAME (init) (struct DYNARRAY_STRUCT *list)
198{
199 list->u.dynarray_header.used = 0;
200 list->u.dynarray_header.allocated = DYNARRAY_INITIAL_SIZE;
201 list->u.dynarray_header.array = DYNARRAY_SCRATCH (list);
202}
203
204/* Deallocate the dynamic array and its elements. */
205__attribute_maybe_unused__ __nonnull ((1))
206static void
207DYNARRAY_FREE (struct DYNARRAY_STRUCT *list)
208{
209 DYNARRAY_NAME (free__elements__)
210 (list->u.dynarray_header.array, list->u.dynarray_header.used);
211 DYNARRAY_NAME (free__array__) (list);
212 DYNARRAY_NAME (init) (list);
213}
214
215/* Return true if the dynamic array is in an error state. */
216__nonnull ((1))
217static inline bool
218DYNARRAY_NAME (has_failed) (const struct DYNARRAY_STRUCT *list)
219{
220 return list->u.dynarray_header.allocated == __dynarray_error_marker ();
221}
222
223/* Mark the dynamic array as failed. All elements are deallocated as
224 a side effect. */
225__nonnull ((1))
226static void
227DYNARRAY_NAME (mark_failed) (struct DYNARRAY_STRUCT *list)
228{
229 DYNARRAY_NAME (free__elements__)
230 (list->u.dynarray_header.array, list->u.dynarray_header.used);
231 DYNARRAY_NAME (free__array__) (list);
232 list->u.dynarray_header.array = DYNARRAY_SCRATCH (list);
233 list->u.dynarray_header.used = 0;
234 list->u.dynarray_header.allocated = __dynarray_error_marker ();
235}
236
237/* Return the number of elements which have been added to the dynamic
238 array. */
239__nonnull ((1))
240static inline size_t
241DYNARRAY_NAME (size) (const struct DYNARRAY_STRUCT *list)
242{
243 return list->u.dynarray_header.used;
244}
245
246/* Return a pointer to the array element at INDEX. Terminate the
247 process if INDEX is out of bounds. */
248__nonnull ((1))
249static inline DYNARRAY_ELEMENT *
250DYNARRAY_NAME (at) (struct DYNARRAY_STRUCT *list, size_t index)
251{
252 if (__glibc_unlikely (index >= DYNARRAY_NAME (size) (list)))
253 __libc_dynarray_at_failure (DYNARRAY_NAME (size) (list), index);
254 return list->u.dynarray_header.array + index;
255}
256
257/* Return a pointer to the first array element, if any. For a
258 zero-length array, the pointer can be NULL even though the dynamic
259 array has not entered the failure state. */
260__nonnull ((1))
261static inline DYNARRAY_ELEMENT *
262DYNARRAY_NAME (begin) (struct DYNARRAY_STRUCT *list)
263{
264 return list->u.dynarray_header.array;
265}
266
267/* Return a pointer one element past the last array element. For a
268 zero-length array, the pointer can be NULL even though the dynamic
269 array has not entered the failure state. */
270__nonnull ((1))
271static inline DYNARRAY_ELEMENT *
272DYNARRAY_NAME (end) (struct DYNARRAY_STRUCT *list)
273{
274 return list->u.dynarray_header.array + list->u.dynarray_header.used;
275}
276
277/* Internal function. Slow path for the add function below. */
278static void
279DYNARRAY_NAME (add__) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
280{
281 if (__glibc_unlikely
282 (!__libc_dynarray_emplace_enlarge (&list->u.dynarray_abstract,
283 DYNARRAY_SCRATCH (list),
284 sizeof (DYNARRAY_ELEMENT))))
285 {
286 DYNARRAY_NAME (mark_failed) (list);
287 return;
288 }
289
290 /* Copy the new element and increase the array length. */
291 list->u.dynarray_header.array[list->u.dynarray_header.used++] = item;
292}
293
294/* Add ITEM at the end of the array, enlarging it by one element.
295 Mark *LIST as failed if the dynamic array allocation size cannot be
296 increased. */
297__nonnull ((1))
298static inline void
299DYNARRAY_NAME (add) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
300{
301 /* Do nothing in case of previous error. */
302 if (DYNARRAY_NAME (has_failed) (list))
303 return;
304
305 /* Enlarge the array if necessary. */
306 if (__glibc_unlikely (list->u.dynarray_header.used
307 == list->u.dynarray_header.allocated))
308 {
309 DYNARRAY_NAME (add__) (list, item);
310 return;
311 }
312
313 /* Copy the new element and increase the array length. */
314 list->u.dynarray_header.array[list->u.dynarray_header.used++] = item;
315}
316
317/* Internal function. Building block for the emplace functions below.
318 Assumes space for one more element in *LIST. */
319static inline DYNARRAY_ELEMENT *
320DYNARRAY_NAME (emplace__tail__) (struct DYNARRAY_STRUCT *list)
321{
322 DYNARRAY_ELEMENT *result
323 = &list->u.dynarray_header.array[list->u.dynarray_header.used];
324 ++list->u.dynarray_header.used;
325#if defined (DYNARRAY_ELEMENT_INIT)
326 DYNARRAY_ELEMENT_INIT (result);
327#elif defined (DYNARRAY_ELEMENT_FREE)
328 memset (result, 0, sizeof (*result));
329#endif
330 return result;
331}
332
333/* Internal function. Slow path for the emplace function below. */
334static DYNARRAY_ELEMENT *
335DYNARRAY_NAME (emplace__) (struct DYNARRAY_STRUCT *list)
336{
337 if (__glibc_unlikely
338 (!__libc_dynarray_emplace_enlarge (&list->u.dynarray_abstract,
339 DYNARRAY_SCRATCH (list),
340 sizeof (DYNARRAY_ELEMENT))))
341 {
342 DYNARRAY_NAME (mark_failed) (list);
343 return NULL;
344 }
345 return DYNARRAY_NAME (emplace__tail__) (list);
346}
347
348/* Allocate a place for a new element in *LIST and return a pointer to
349 it. The pointer can be NULL if the dynamic array cannot be
350 enlarged due to a memory allocation failure. */
351__attribute_maybe_unused__ __attribute_warn_unused_result__ __nonnull ((1))
352static
353/* Avoid inlining with the larger initialization code. */
354#if !(defined (DYNARRAY_ELEMENT_INIT) || defined (DYNARRAY_ELEMENT_FREE))
355inline
356#endif
357DYNARRAY_ELEMENT *
358DYNARRAY_NAME (emplace) (struct DYNARRAY_STRUCT *list)
359{
360 /* Do nothing in case of previous error. */
361 if (DYNARRAY_NAME (has_failed) (list))
362 return NULL;
363
364 /* Enlarge the array if necessary. */
365 if (__glibc_unlikely (list->u.dynarray_header.used
366 == list->u.dynarray_header.allocated))
367 return (DYNARRAY_NAME (emplace__) (list));
368 return DYNARRAY_NAME (emplace__tail__) (list);
369}
370
371/* Change the size of *LIST to SIZE. If SIZE is larger than the
372 existing size, new elements are added (which can be initialized).
373 Otherwise, the list is truncated, and elements are freed. Return
374 false on memory allocation failure (and mark *LIST as failed). */
375__attribute_maybe_unused__ __nonnull ((1))
376static bool
377DYNARRAY_NAME (resize) (struct DYNARRAY_STRUCT *list, size_t size)
378{
379 if (size > list->u.dynarray_header.used)
380 {
381 bool ok;
382#if defined (DYNARRAY_ELEMENT_INIT)
383 /* The new elements have to be initialized. */
384 size_t old_size = list->u.dynarray_header.used;
385 ok = __libc_dynarray_resize (&list->u.dynarray_abstract,
386 size, DYNARRAY_SCRATCH (list),
387 sizeof (DYNARRAY_ELEMENT));
388 if (ok)
389 for (size_t i = old_size; i < size; ++i)
390 {
391 DYNARRAY_ELEMENT_INIT (&list->u.dynarray_header.array[i]);
392 }
393#elif defined (DYNARRAY_ELEMENT_FREE)
394 /* Zero initialization is needed so that the elements can be
395 safely freed. */
396 ok = __libc_dynarray_resize_clear
397 (&list->u.dynarray_abstract, size,
398 DYNARRAY_SCRATCH (list), sizeof (DYNARRAY_ELEMENT));
399#else
400 ok = __libc_dynarray_resize (&list->u.dynarray_abstract,
401 size, DYNARRAY_SCRATCH (list),
402 sizeof (DYNARRAY_ELEMENT));
403#endif
404 if (__glibc_unlikely (!ok))
405 DYNARRAY_NAME (mark_failed) (list);
406 return ok;
407 }
408 else
409 {
410 /* The list has shrunk in size. Free the removed elements. */
411 DYNARRAY_NAME (free__elements__)
412 (list->u.dynarray_header.array + size,
413 list->u.dynarray_header.used - size);
414 list->u.dynarray_header.used = size;
415 return true;
416 }
417}
418
419/* Remove the last element of LIST if it is present. */
420__attribute_maybe_unused__ __nonnull ((1))
421static void
422DYNARRAY_NAME (remove_last) (struct DYNARRAY_STRUCT *list)
423{
424 /* used > 0 implies that the array is the non-failed state. */
425 if (list->u.dynarray_header.used > 0)
426 {
427 size_t new_length = list->u.dynarray_header.used - 1;
428#ifdef DYNARRAY_ELEMENT_FREE
429 DYNARRAY_ELEMENT_FREE (&list->u.dynarray_header.array[new_length]);
430#endif
431 list->u.dynarray_header.used = new_length;
432 }
433}
434
435/* Remove all elements from the list. The elements are freed, but the
436 list itself is not. */
437__attribute_maybe_unused__ __nonnull ((1))
438static void
439DYNARRAY_NAME (clear) (struct DYNARRAY_STRUCT *list)
440{
441 /* free__elements__ does nothing if the list is in the failed
442 state. */
443 DYNARRAY_NAME (free__elements__)
444 (list->u.dynarray_header.array, list->u.dynarray_header.used);
445 list->u.dynarray_header.used = 0;
446}
447
448#ifdef DYNARRAY_FINAL_TYPE
449/* Transfer the dynamic array to a permanent location at *RESULT.
450 Returns true on success on false on allocation failure. In either
451 case, *LIST is re-initialized and can be reused. A NULL pointer is
452 stored in *RESULT if LIST refers to an empty list. On success, the
453 pointer in *RESULT is heap-allocated and must be deallocated using
454 free. */
455__attribute_maybe_unused__ __attribute_warn_unused_result__ __nonnull ((1, 2))
456static bool
457DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list,
458 DYNARRAY_FINAL_TYPE *result)
459{
460 struct dynarray_finalize_result res;
461 if (__libc_dynarray_finalize (&list->u.dynarray_abstract,
462 DYNARRAY_SCRATCH (list),
463 sizeof (DYNARRAY_ELEMENT), &res))
464 {
465 /* On success, the result owns all the data. */
466 DYNARRAY_NAME (init) (list);
467 *result = (DYNARRAY_FINAL_TYPE) { res.array, res.length };
468 return true;
469 }
470 else
471 {
472 /* On error, we need to free all data. */
473 DYNARRAY_FREE (list);
474 errno = ENOMEM;
475 return false;
476 }
477}
478#else /* !DYNARRAY_FINAL_TYPE */
479/* Transfer the dynamic array to a heap-allocated array and return a
480 pointer to it. The pointer is NULL if memory allocation fails, or
481 if the array is empty, so this function should be used only for
482 arrays which are known not be empty (usually because they always
483 have a sentinel at the end). If LENGTHP is not NULL, the array
484 length is written to *LENGTHP. *LIST is re-initialized and can be
485 reused. */
486__attribute_maybe_unused__ __attribute_warn_unused_result__ __nonnull ((1))
487static DYNARRAY_ELEMENT *
488DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list, size_t *lengthp)
489{
490 struct dynarray_finalize_result res;
491 if (__libc_dynarray_finalize (&list->u.dynarray_abstract,
492 DYNARRAY_SCRATCH (list),
493 sizeof (DYNARRAY_ELEMENT), &res))
494 {
495 /* On success, the result owns all the data. */
496 DYNARRAY_NAME (init) (list);
497 if (lengthp != NULL)
498 *lengthp = res.length;
499 return res.array;
500 }
501 else
502 {
503 /* On error, we need to free all data. */
504 DYNARRAY_FREE (list);
505 errno = ENOMEM;
506 return NULL;
507 }
508}
509#endif /* !DYNARRAY_FINAL_TYPE */
510
511/* Undo macro definitions. */
512
513#undef DYNARRAY_CONCAT0
514#undef DYNARRAY_CONCAT1
515#undef DYNARRAY_NAME
516#undef DYNARRAY_SCRATCH
517#undef DYNARRAY_HAVE_SCRATCH
518
519#undef DYNARRAY_STRUCT
520#undef DYNARRAY_ELEMENT
521#undef DYNARRAY_PREFIX
522#undef DYNARRAY_ELEMENT_FREE
523#undef DYNARRAY_ELEMENT_INIT
524#undef DYNARRAY_INITIAL_SIZE
525#undef DYNARRAY_FINAL_TYPE
diff --git a/lib/malloc/dynarray.h b/lib/malloc/dynarray.h
new file mode 100644
index 00000000000..84e4394bf32
--- /dev/null
+++ b/lib/malloc/dynarray.h
@@ -0,0 +1,178 @@
1/* Type-safe arrays which grow dynamically. Shared definitions.
2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* To use the dynarray facility, you need to include
20 <malloc/dynarray-skeleton.c> and define the parameter macros
21 documented in that file.
22
23 A minimal example which provides a growing list of integers can be
24 defined like this:
25
26 struct int_array
27 {
28 // Pointer to result array followed by its length,
29 // as required by DYNARRAY_FINAL_TYPE.
30 int *array;
31 size_t length;
32 };
33
34 #define DYNARRAY_STRUCT dynarray_int
35 #define DYNARRAY_ELEMENT int
36 #define DYNARRAY_PREFIX dynarray_int_
37 #define DYNARRAY_FINAL_TYPE struct int_array
38 #include <malloc/dynarray-skeleton.c>
39
40 To create a three-element array with elements 1, 2, 3, use this
41 code:
42
43 struct dynarray_int dyn;
44 dynarray_int_init (&dyn);
45 for (int i = 1; i <= 3; ++i)
46 {
47 int *place = dynarray_int_emplace (&dyn);
48 assert (place != NULL);
49 *place = i;
50 }
51 struct int_array result;
52 bool ok = dynarray_int_finalize (&dyn, &result);
53 assert (ok);
54 assert (result.length == 3);
55 assert (result.array[0] == 1);
56 assert (result.array[1] == 2);
57 assert (result.array[2] == 3);
58 free (result.array);
59
60 If the elements contain resources which must be freed, define
61 DYNARRAY_ELEMENT_FREE appropriately, like this:
62
63 struct str_array
64 {
65 char **array;
66 size_t length;
67 };
68
69 #define DYNARRAY_STRUCT dynarray_str
70 #define DYNARRAY_ELEMENT char *
71 #define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
72 #define DYNARRAY_PREFIX dynarray_str_
73 #define DYNARRAY_FINAL_TYPE struct str_array
74 #include <malloc/dynarray-skeleton.c>
75
76 Compared to scratch buffers, dynamic arrays have the following
77 features:
78
79 - They have an element type, and are not just an untyped buffer of
80 bytes.
81
82 - When growing, previously stored elements are preserved. (It is
83 expected that scratch_buffer_grow_preserve and
84 scratch_buffer_set_array_size eventually go away because all
85 current users are moved to dynamic arrays.)
86
87 - Scratch buffers have a more aggressive growth policy because
88 growing them typically means a retry of an operation (across an
89 NSS service module boundary), which is expensive.
90
91 - For the same reason, scratch buffers have a much larger initial
92 stack allocation. */
93
94#ifndef _DYNARRAY_H
95#define _DYNARRAY_H
96
97#include <stdbool.h>
98#include <stddef.h>
99#include <string.h>
100
101struct dynarray_header
102{
103 size_t used;
104 size_t allocated;
105 void *array;
106};
107
108/* Marker used in the allocated member to indicate that an error was
109 encountered. */
110static inline size_t
111__dynarray_error_marker (void)
112{
113 return -1;
114}
115
116/* Internal function. See the has_failed function in
117 dynarray-skeleton.c. */
118static inline bool
119__dynarray_error (struct dynarray_header *list)
120{
121 return list->allocated == __dynarray_error_marker ();
122}
123
124/* Internal function. Enlarge the dynamically allocated area of the
125 array to make room for one more element. SCRATCH is a pointer to
126 the scratch area (which is not heap-allocated and must not be
127 freed). ELEMENT_SIZE is the size, in bytes, of one element.
128 Return false on failure, true on success. */
129bool __libc_dynarray_emplace_enlarge (struct dynarray_header *,
130 void *scratch, size_t element_size);
131
132/* Internal function. Enlarge the dynamically allocated area of the
133 array to make room for at least SIZE elements (which must be larger
134 than the existing used part of the dynamic array). SCRATCH is a
135 pointer to the scratch area (which is not heap-allocated and must
136 not be freed). ELEMENT_SIZE is the size, in bytes, of one element.
137 Return false on failure, true on success. */
138bool __libc_dynarray_resize (struct dynarray_header *, size_t size,
139 void *scratch, size_t element_size);
140
141/* Internal function. Like __libc_dynarray_resize, but clear the new
142 part of the dynamic array. */
143bool __libc_dynarray_resize_clear (struct dynarray_header *, size_t size,
144 void *scratch, size_t element_size);
145
146/* Internal type. */
147struct dynarray_finalize_result
148{
149 void *array;
150 size_t length;
151};
152
153/* Internal function. Copy the dynamically-allocated area to an
154 explicitly-sized heap allocation. SCRATCH is a pointer to the
155 embedded scratch space. ELEMENT_SIZE is the size, in bytes, of the
156 element type. On success, true is returned, and pointer and length
157 are written to *RESULT. On failure, false is returned. The caller
158 has to take care of some of the memory management; this function is
159 expected to be called from dynarray-skeleton.c. */
160bool __libc_dynarray_finalize (struct dynarray_header *list, void *scratch,
161 size_t element_size,
162 struct dynarray_finalize_result *result);
163
164
165/* Internal function. Terminate the process after an index error.
166 SIZE is the number of elements of the dynamic array. INDEX is the
167 lookup index which triggered the failure. */
168_Noreturn void __libc_dynarray_at_failure (size_t size, size_t index);
169
170#ifndef _ISOMAC
171libc_hidden_proto (__libc_dynarray_emplace_enlarge)
172libc_hidden_proto (__libc_dynarray_resize)
173libc_hidden_proto (__libc_dynarray_resize_clear)
174libc_hidden_proto (__libc_dynarray_finalize)
175libc_hidden_proto (__libc_dynarray_at_failure)
176#endif
177
178#endif /* _DYNARRAY_H */
diff --git a/lib/malloc/dynarray_at_failure.c b/lib/malloc/dynarray_at_failure.c
new file mode 100644
index 00000000000..a4424593748
--- /dev/null
+++ b/lib/malloc/dynarray_at_failure.c
@@ -0,0 +1,35 @@
1/* Report an dynamic array index out of bounds condition.
2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <dynarray.h>
20#include <stdio.h>
21#include <stdlib.h>
22
23void
24__libc_dynarray_at_failure (size_t size, size_t index)
25{
26#ifdef _LIBC
27 char buf[200];
28 __snprintf (buf, sizeof (buf), "Fatal glibc error: "
29 "array index %zu not less than array length %zu\n",
30 index, size);
31#else
32 abort ();
33#endif
34}
35libc_hidden_def (__libc_dynarray_at_failure)
diff --git a/lib/malloc/dynarray_emplace_enlarge.c b/lib/malloc/dynarray_emplace_enlarge.c
new file mode 100644
index 00000000000..7ac4b6db403
--- /dev/null
+++ b/lib/malloc/dynarray_emplace_enlarge.c
@@ -0,0 +1,73 @@
1/* Increase the size of a dynamic array in preparation of an emplace operation.
2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <dynarray.h>
20#include <errno.h>
21#include <intprops.h>
22#include <stdlib.h>
23#include <string.h>
24
25bool
26__libc_dynarray_emplace_enlarge (struct dynarray_header *list,
27 void *scratch, size_t element_size)
28{
29 size_t new_allocated;
30 if (list->allocated == 0)
31 {
32 /* No scratch buffer provided. Choose a reasonable default
33 size. */
34 if (element_size < 4)
35 new_allocated = 16;
36 else if (element_size < 8)
37 new_allocated = 8;
38 else
39 new_allocated = 4;
40 }
41 else
42 /* Increase the allocated size, using an exponential growth
43 policy. */
44 {
45 new_allocated = list->allocated + list->allocated / 2 + 1;
46 if (new_allocated <= list->allocated)
47 {
48 /* Overflow. */
49 __set_errno (ENOMEM);
50 return false;
51 }
52 }
53
54 size_t new_size;
55 if (INT_MULTIPLY_WRAPV (new_allocated, element_size, &new_size))
56 return false;
57 void *new_array;
58 if (list->array == scratch)
59 {
60 /* The previous array was not heap-allocated. */
61 new_array = malloc (new_size);
62 if (new_array != NULL && list->array != NULL)
63 memcpy (new_array, list->array, list->used * element_size);
64 }
65 else
66 new_array = realloc (list->array, new_size);
67 if (new_array == NULL)
68 return false;
69 list->array = new_array;
70 list->allocated = new_allocated;
71 return true;
72}
73libc_hidden_def (__libc_dynarray_emplace_enlarge)
diff --git a/lib/malloc/dynarray_finalize.c b/lib/malloc/dynarray_finalize.c
new file mode 100644
index 00000000000..be9441e313d
--- /dev/null
+++ b/lib/malloc/dynarray_finalize.c
@@ -0,0 +1,62 @@
1/* Copy the dynamically-allocated area to an explicitly-sized heap allocation.
2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <dynarray.h>
20#include <stdlib.h>
21#include <string.h>
22
23bool
24__libc_dynarray_finalize (struct dynarray_header *list,
25 void *scratch, size_t element_size,
26 struct dynarray_finalize_result *result)
27{
28 if (__dynarray_error (list))
29 /* The caller will reported the deferred error. */
30 return false;
31
32 size_t used = list->used;
33
34 /* Empty list. */
35 if (used == 0)
36 {
37 /* An empty list could still be backed by a heap-allocated
38 array. Free it if necessary. */
39 if (list->array != scratch)
40 free (list->array);
41 *result = (struct dynarray_finalize_result) { NULL, 0 };
42 return true;
43 }
44
45 size_t allocation_size = used * element_size;
46 void *heap_array = malloc (allocation_size);
47 if (heap_array != NULL)
48 {
49 /* The new array takes ownership of the strings. */
50 if (list->array != NULL)
51 memcpy (heap_array, list->array, allocation_size);
52 if (list->array != scratch)
53 free (list->array);
54 *result = (struct dynarray_finalize_result)
55 { .array = heap_array, .length = used };
56 return true;
57 }
58 else
59 /* The caller will perform the freeing operation. */
60 return false;
61}
62libc_hidden_def (__libc_dynarray_finalize)
diff --git a/lib/malloc/dynarray_resize.c b/lib/malloc/dynarray_resize.c
new file mode 100644
index 00000000000..92bbddd4461
--- /dev/null
+++ b/lib/malloc/dynarray_resize.c
@@ -0,0 +1,64 @@
1/* Increase the size of a dynamic array.
2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <dynarray.h>
20#include <errno.h>
21#include <intprops.h>
22#include <stdlib.h>
23#include <string.h>
24
25bool
26__libc_dynarray_resize (struct dynarray_header *list, size_t size,
27 void *scratch, size_t element_size)
28{
29 /* The existing allocation provides sufficient room. */
30 if (size <= list->allocated)
31 {
32 list->used = size;
33 return true;
34 }
35
36 /* Otherwise, use size as the new allocation size. The caller is
37 expected to provide the final size of the array, so there is no
38 over-allocation here. */
39
40 size_t new_size_bytes;
41 if (INT_MULTIPLY_WRAPV (size, element_size, &new_size_bytes))
42 {
43 /* Overflow. */
44 __set_errno (ENOMEM);
45 return false;
46 }
47 void *new_array;
48 if (list->array == scratch)
49 {
50 /* The previous array was not heap-allocated. */
51 new_array = malloc (new_size_bytes);
52 if (new_array != NULL && list->array != NULL)
53 memcpy (new_array, list->array, list->used * element_size);
54 }
55 else
56 new_array = realloc (list->array, new_size_bytes);
57 if (new_array == NULL)
58 return false;
59 list->array = new_array;
60 list->allocated = size;
61 list->used = size;
62 return true;
63}
64libc_hidden_def (__libc_dynarray_resize)
diff --git a/lib/malloc/dynarray_resize_clear.c b/lib/malloc/dynarray_resize_clear.c
new file mode 100644
index 00000000000..99c2cc87c31
--- /dev/null
+++ b/lib/malloc/dynarray_resize_clear.c
@@ -0,0 +1,35 @@
1/* Increase the size of a dynamic array and clear the new part.
2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <dynarray.h>
20#include <string.h>
21
22bool
23__libc_dynarray_resize_clear (struct dynarray_header *list, size_t size,
24 void *scratch, size_t element_size)
25{
26 size_t old_size = list->used;
27 if (!__libc_dynarray_resize (list, size, scratch, element_size))
28 return false;
29 /* __libc_dynarray_resize already checked for overflow. */
30 char *array = list->array;
31 memset (array + (old_size * element_size), 0,
32 (size - old_size) * element_size);
33 return true;
34}
35libc_hidden_def (__libc_dynarray_resize_clear)
diff --git a/lib/malloc/scratch_buffer_grow.c b/lib/malloc/scratch_buffer_grow.c
index 41befe3d65f..e7606d81cd7 100644
--- a/lib/malloc/scratch_buffer_grow.c
+++ b/lib/malloc/scratch_buffer_grow.c
@@ -1,5 +1,5 @@
1/* Variable-sized buffer with on-stack default allocation. 1/* Variable-sized buffer with on-stack default allocation.
2 Copyright (C) 2015-2020 Free Software Foundation, Inc. 2 Copyright (C) 2015-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library. 3 This file is part of the GNU C Library.
4 4
5 The GNU C Library is free software; you can redistribute it and/or 5 The GNU C Library is free software; you can redistribute it and/or
diff --git a/lib/malloc/scratch_buffer_grow_preserve.c b/lib/malloc/scratch_buffer_grow_preserve.c
index aef232938d5..59f8c710001 100644
--- a/lib/malloc/scratch_buffer_grow_preserve.c
+++ b/lib/malloc/scratch_buffer_grow_preserve.c
@@ -1,5 +1,5 @@
1/* Variable-sized buffer with on-stack default allocation. 1/* Variable-sized buffer with on-stack default allocation.
2 Copyright (C) 2015-2020 Free Software Foundation, Inc. 2 Copyright (C) 2015-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library. 3 This file is part of the GNU C Library.
4 4
5 The GNU C Library is free software; you can redistribute it and/or 5 The GNU C Library is free software; you can redistribute it and/or
diff --git a/lib/malloc/scratch_buffer_set_array_size.c b/lib/malloc/scratch_buffer_set_array_size.c
index 5f5e4c24f5a..e2b9f31211a 100644
--- a/lib/malloc/scratch_buffer_set_array_size.c
+++ b/lib/malloc/scratch_buffer_set_array_size.c
@@ -1,5 +1,5 @@
1/* Variable-sized buffer with on-stack default allocation. 1/* Variable-sized buffer with on-stack default allocation.
2 Copyright (C) 2015-2020 Free Software Foundation, Inc. 2 Copyright (C) 2015-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library. 3 This file is part of the GNU C Library.
4 4
5 The GNU C Library is free software; you can redistribute it and/or 5 The GNU C Library is free software; you can redistribute it and/or