1 | /* Variable-sized buffer with on-stack default allocation. |
2 | Copyright (C) 2015-2024 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 (ptr: 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 */ |
136 | |