1 | /* Variable-sized buffer with on-stack default allocation.
|
---|
2 | Copyright (C) 2015-2022 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 Lesser General Public
|
---|
7 | License as published by the Free Software Foundation; either
|
---|
8 | version 2.1 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 | Lesser General Public License for more details.
|
---|
14 |
|
---|
15 | You should have received a copy of the GNU Lesser General Public
|
---|
16 | License along with the GNU C Library; if not, see
|
---|
17 | <https://www.gnu.org/licenses/>. */
|
---|
18 |
|
---|
19 | #ifndef _SCRATCH_BUFFER_H
|
---|
20 | #define _SCRATCH_BUFFER_H
|
---|
21 |
|
---|
22 | /* Scratch buffers with a default stack allocation and fallback to
|
---|
23 | heap allocation. It is expected that this function is used in this
|
---|
24 | way:
|
---|
25 |
|
---|
26 | struct scratch_buffer tmpbuf;
|
---|
27 | scratch_buffer_init (&tmpbuf);
|
---|
28 |
|
---|
29 | while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
|
---|
30 | if (!scratch_buffer_grow (&tmpbuf))
|
---|
31 | return -1;
|
---|
32 |
|
---|
33 | scratch_buffer_free (&tmpbuf);
|
---|
34 | return 0;
|
---|
35 |
|
---|
36 | The allocation functions (scratch_buffer_grow,
|
---|
37 | scratch_buffer_grow_preserve, scratch_buffer_set_array_size) make
|
---|
38 | sure that the heap allocation, if any, is freed, so that the code
|
---|
39 | above does not have a memory leak. The buffer still remains in a
|
---|
40 | state that can be deallocated using scratch_buffer_free, so a loop
|
---|
41 | like this is valid as well:
|
---|
42 |
|
---|
43 | struct scratch_buffer tmpbuf;
|
---|
44 | scratch_buffer_init (&tmpbuf);
|
---|
45 |
|
---|
46 | while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
|
---|
47 | if (!scratch_buffer_grow (&tmpbuf))
|
---|
48 | break;
|
---|
49 |
|
---|
50 | scratch_buffer_free (&tmpbuf);
|
---|
51 |
|
---|
52 | scratch_buffer_grow and scratch_buffer_grow_preserve are guaranteed
|
---|
53 | to grow the buffer by at least 512 bytes. This means that when
|
---|
54 | using the scratch buffer as a backing store for a non-character
|
---|
55 | array whose element size, in bytes, is 512 or smaller, the scratch
|
---|
56 | buffer only has to grow once to make room for at least one more
|
---|
57 | element.
|
---|
58 | */
|
---|
59 |
|
---|
60 | #include <stdbool.h>
|
---|
61 | #include <stddef.h>
|
---|
62 | #include <stdlib.h>
|
---|
63 |
|
---|
64 | /* Scratch buffer. Must be initialized with scratch_buffer_init
|
---|
65 | before its use. */
|
---|
66 | struct scratch_buffer {
|
---|
67 | void *data; /* Pointer to the beginning of the scratch area. */
|
---|
68 | size_t length; /* Allocated space at the data pointer, in bytes. */
|
---|
69 | union { max_align_t __align; char __c[1024]; } __space;
|
---|
70 | };
|
---|
71 |
|
---|
72 | /* Initializes *BUFFER so that BUFFER->data points to BUFFER->__space
|
---|
73 | and BUFFER->length reflects the available space. */
|
---|
74 | static inline void
|
---|
75 | scratch_buffer_init (struct scratch_buffer *buffer)
|
---|
76 | {
|
---|
77 | buffer->data = buffer->__space.__c;
|
---|
78 | buffer->length = sizeof (buffer->__space);
|
---|
79 | }
|
---|
80 |
|
---|
81 | /* Deallocates *BUFFER (if it was heap-allocated). */
|
---|
82 | static inline void
|
---|
83 | scratch_buffer_free (struct scratch_buffer *buffer)
|
---|
84 | {
|
---|
85 | if (buffer->data != buffer->__space.__c)
|
---|
86 | free (buffer->data);
|
---|
87 | }
|
---|
88 |
|
---|
89 | /* Grow *BUFFER by some arbitrary amount. The buffer contents is NOT
|
---|
90 | preserved. Return true on success, false on allocation failure (in
|
---|
91 | which case the old buffer is freed). On success, the new buffer is
|
---|
92 | larger than the previous size. On failure, *BUFFER is deallocated,
|
---|
93 | but remains in a free-able state, and errno is set. */
|
---|
94 | bool __libc_scratch_buffer_grow (struct scratch_buffer *buffer);
|
---|
95 | libc_hidden_proto (__libc_scratch_buffer_grow)
|
---|
96 |
|
---|
97 | /* Alias for __libc_scratch_buffer_grow. */
|
---|
98 | static __always_inline bool
|
---|
99 | scratch_buffer_grow (struct scratch_buffer *buffer)
|
---|
100 | {
|
---|
101 | return __glibc_likely (__libc_scratch_buffer_grow (buffer));
|
---|
102 | }
|
---|
103 |
|
---|
104 | /* Like __libc_scratch_buffer_grow, but preserve the old buffer
|
---|
105 | contents on success, as a prefix of the new buffer. */
|
---|
106 | bool __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer);
|
---|
107 | libc_hidden_proto (__libc_scratch_buffer_grow_preserve)
|
---|
108 |
|
---|
109 | /* Alias for __libc_scratch_buffer_grow_preserve. */
|
---|
110 | static __always_inline bool
|
---|
111 | scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
|
---|
112 | {
|
---|
113 | return __glibc_likely (__libc_scratch_buffer_grow_preserve (buffer));
|
---|
114 | }
|
---|
115 |
|
---|
116 | /* Grow *BUFFER so that it can store at least NELEM elements of SIZE
|
---|
117 | bytes. The buffer contents are NOT preserved. Both NELEM and SIZE
|
---|
118 | can be zero. Return true on success, false on allocation failure
|
---|
119 | (in which case the old buffer is freed, but *BUFFER remains in a
|
---|
120 | free-able state, and errno is set). It is unspecified whether this
|
---|
121 | function can reduce the array size. */
|
---|
122 | bool __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
|
---|
123 | size_t nelem, size_t size);
|
---|
124 | libc_hidden_proto (__libc_scratch_buffer_set_array_size)
|
---|
125 |
|
---|
126 | /* Alias for __libc_scratch_set_array_size. */
|
---|
127 | static __always_inline bool
|
---|
128 | scratch_buffer_set_array_size (struct scratch_buffer *buffer,
|
---|
129 | size_t nelem, size_t size)
|
---|
130 | {
|
---|
131 | return __glibc_likely (__libc_scratch_buffer_set_array_size
|
---|
132 | (buffer, nelem, size));
|
---|
133 | }
|
---|
134 |
|
---|
135 | #endif /* _SCRATCH_BUFFER_H */
|
---|