chiark / gitweb /
@@@ fltfmt wip
[mLib] / mem / growbuf.h
1 /* -*-c-*-
2  *
3  * Grow a buffer if it's too small
4  *
5  * (c) 2024 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU Library General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or (at
15  * your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
20  * License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib.  If not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25  * USA.
26  */
27
28 #ifndef MLIB_GROWBUF_H
29 #define MLIB_GROWBUF_H
30
31 #ifdef __cplusplus
32   extern "C" {
33 #endif
34
35 /*----- Header files ------------------------------------------------------*/
36
37 #include <assert.h>
38 #include <stddef.h>
39
40 #ifndef MLIB_ALLOC_H
41 #  include "alloc.h"
42 #endif
43
44 /*----- Macros provided ---------------------------------------------------*/
45
46 /* --- @GROWBUF__LIMIT@ --- *
47  *
48  * Arguments:   @szty@ = an unsigned type
49  *              @size_t granule@ = allocation granule
50  *
51  * Returns:     The largest number %$n$% such that %$n$% is representable in
52  *              a value of the given @szty@ and the total size of %$n$%
53  *              objects, each of size @granule@, can be represented in a
54  *              value of type @size_t@.
55  */
56
57 #define GROWBUF__LIMIT(szty, granule)                                   \
58         (~(szty)0 < ~(size_t)0/(granule) ?                              \
59          ~(szty)0 : ~(size_t)0/(granule))
60
61 /* --- @GROWBUF_SIZE@ --- *
62  *
63  * Arguments:   @szty@ = an unsigned type
64  *              @szty sz@ = the current size (updated)
65  *              @szty want@ = the desired minimum size
66  *              @szty init@ = a suitable initial size
67  *              @szty granule@ = the allocation granule size
68  *
69  * Returns:     ---
70  *
71  * Use:         On entry, @sz@ should be the current capacity of some buffer,
72  *              in terms of objects of size @granule@, and @want@ a needed
73  *              capacity, in the same terms, with @want > sz@; @init@ should
74  *              be some suitable positive initial size, in case the current
75  *              size is zero.  The macro updates @sz@ to be some suitable new
76  *              positive size at least as large as @want@.
77  */
78
79 #define GROWBUF_SIZE(szty, sz, want, init, granule) do {                \
80   szty _sz_ = (sz), _want_ = (want);                                    \
81                                                                         \
82   assert(_want_ < GROWBUF__LIMIT(szty, granule)/2);                     \
83   if (!_sz_) _sz_ = (init);                                             \
84   while (_sz_ < _want_) _sz_ *= 2;                                      \
85   (sz) = _sz_;                                                          \
86 } while (0)
87
88 /* --- @GROWBUF_EXTEND@, @GROWBUF_REPLACE@ --- *
89  *
90  * Arguments:   @szty@ = an unsigned type
91  *              @arena *a@ = pointer to an arena
92  *              @type *buf@ = pointer to some buffer, possibly null (updated)
93  *              @szty sz@ = current buffer size (updated)
94  *              @szty want@ = desired minimum size
95  *              @szty init@ = a suitable initial size
96  *              @size_t granule@ = the allocation granule size
97  *
98  * Returns:     ---
99  *
100  * Use:         On entry, @buf@ should be a pointer to a buffer, allocated
101  *              from the arena @a@, with space for @sz@ objects of size
102  *              @granule@; @buf@ may be null if @sz@ is zero.  On exit, @buf@
103  *              and @sz@ will be updated to refer to a possibly different
104  *              buffer, with space for at least @want@ objects (but certainly
105  *              not smaller than before).
106  *
107  *              @GROWBUF_EXTEND@ preserves the contents of the buffer;
108  *              @GROWBUF_REPLACE@ discards the existing contents.
109  */
110
111 #define GROWBUF_EXTEND(szty, a, buf, sz, want, init, granule) do {      \
112   szty _sz0 = (sz), _sz = _sz0, _want = (want);                         \
113   size_t _gr = (granule);                                               \
114   void *_p = (buf);                                                     \
115   arena *_a = (a);                                                      \
116                                                                         \
117   if (_sz < _want) {                                                    \
118     GROWBUF_SIZE(szty, _sz, _want, init, _gr);                          \
119     if (!_p) _p = x_alloc(_a, _sz*_gr);                                 \
120     else _p = x_realloc(_a, _p, _sz*_gr, _sz0*_gr);                     \
121     (buf) = _p; (sz) = _sz;                                             \
122   }                                                                     \
123 } while (0)
124
125 #define GROWBUF_REPLACE(szty, a, buf, sz, want, init, granule) do {     \
126   szty _sz0 = (sz), _sz = _sz0, _want = (want);                         \
127   size_t _gr = (granule);                                               \
128   void *_p = (buf);                                                     \
129   arena *_a = (a);                                                      \
130                                                                         \
131   if (_sz < _want) {                                                    \
132     GROWBUF_SIZE(szty, _sz, want, init, _gr);                           \
133     if (_p) x_free(_a, _p);                                             \
134     _p = x_alloc(_a, _sz*_gr);                                          \
135     (buf) = _p; (sz) = _sz;                                             \
136   }                                                                     \
137 } while (0)
138
139 /* --- @GROWBUF_RENEWV@, @GROWBUF_REPLACEV@ --- *
140  *
141  * Arguments:   @szty@ = an unsigned type
142  *              @arena *a@ = pointer to an arena
143  *              @type *buf@ = pointer to some buffer, possibly null (updated)
144  *              @szty sz@ = current buffer size (updated)
145  *              @szty want@ = desired minimum size
146  *              @szty init@ = a suitable initial size
147  *
148  * Returns:     ---
149  *
150  * Use:         These are the same as @GROWBUF_EXTEND@ and @GROWBUF_REPLACE@,
151  *              except that the @granule@ is implicitly taken as
152  *              @sizeof(*buf)@.
153  *
154  *              @GROWBUF_RENEWV@ preserves the contents of the buffer;
155  *              @GROWBUF_REPLACEV@ discards the existing contents.
156  */
157
158 #define GROWBUF_RENEWV(szty, a, buf, sz, want, init)                    \
159   GROWBUF_EXTEND(szty, a, buf, sz, want, init, sizeof(*buf))
160
161 #define GROWBUF_REPLACEV(szty, a, buf, sz, want, init)                  \
162   GROWBUF_REPLACE(szty, a, buf, sz, want, init, sizeof(*buf))
163
164 /*----- That's all, folks -------------------------------------------------*/
165
166 #ifdef __cplusplus
167   }
168 #endif
169
170 #endif