chiark / gitweb /
@@@ man 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:   @size_t granule@ = allocation granule
49  *
50  * Returns:     The largest number %$n$% such that the total size of %$n$%
51  *              objects, each of size @granule@, can be represented in a
52  *              @size_t@.
53  */
54
55 #define GROWBUF_LIMIT(granule) (~(size_t)0/(granule))
56
57 /* --- @GROWBUF_SIZE@ --- *
58  *
59  * Arguments:   @size_t sz@ = the current size (updated)
60  *              @size_t want@ = the desired minimum size
61  *              @size_t init@ = a suitable initial size
62  *              @size_t granule@ = the allocation granule size
63  *
64  * Returns:     ---
65  *
66  * Use:         On entry, @sz@ should be the current capacity of some buffer,
67  *              in terms of objects of size @granule@, and @want@ a needed
68  *              capacity, in the same terms, with @want > sz@; @init@ should
69  *              be some suitable positive initial size, in case the current
70  *              size is zero.  The macro updates @sz@ to be some suitable new
71  *              positive size at least as large as @want@.
72  */
73
74 #define GROWBUF_SIZE(sz, want, init, granule) do {                      \
75   size_t _sz_ = (sz), _want_ = (want);                                  \
76                                                                         \
77   assert(_want_ < GROWBUF_LIMIT(granule)/2);                            \
78   if (!_sz_) _sz_ = (init);                                             \
79   while (_sz_ < _want_) _sz_ *= 2;                                      \
80   (sz) = _sz_;                                                          \
81 } while (0)
82
83 /* --- @GROWBUF_EXTEND@, @GROWBUF_REPLACE@ --- *
84  *
85  * Arguments:   @arena *a@ = pointer to an arena
86  *              @type *buf@ = pointer to some buffer, possibly null (updated)
87  *              @size_t sz@ = current buffer size (updated)
88  *              @size_t want@ = desired minimum size
89  *              @size_t init@ = a suitable initial size
90  *              @size_t granule@ = the allocation granule size
91  *
92  * Returns:     ---
93  *
94  * Use:         On entry, @buf@ should be a pointer to a buffer, allocated
95  *              from the arena @a@, with space for @sz@ objects of size
96  *              @granule@; @buf@ may be null if @sz@ is zero.  On exit, @buf@
97  *              and @sz@ will be updated to refer to a possibly different
98  *              buffer, with space for at least @want@ objects (but certainly
99  *              not smaller than before).
100  *
101  *              @GROWBUF_EXTEND@ preserves the contents of the buffer;
102  *              @GROWBUF_REPLACE@ discards the existing contents.
103  */
104
105 #define GROWBUF_EXTEND(a, buf, sz, want, init, granule) do {            \
106   size_t _sz0 = (sz), _sz = _sz0, _want = (want), _gr = (granule);      \
107   void *_p = (buf);                                                     \
108   arena *_a = (a);                                                      \
109                                                                         \
110   if (_sz < _want) {                                                    \
111     GROWBUF_SIZE(_sz, _want, init, _gr);                                \
112     if (!_p) _p = x_alloc(_a, _sz*_gr);                                 \
113     else _p = x_realloc(_a, _p, _sz*_gr, _sz0*_gr);                     \
114     (buf) = _p; (sz) = _sz;                                             \
115   }                                                                     \
116 } while (0)
117
118 #define GROWBUF_REPLACE(a, buf, sz, want, init, granule) do {           \
119   size_t _sz0 = (sz), _sz = _sz0, _want = (want), _gr = (granule);      \
120   void *_p = (buf);                                                     \
121   arena *_a = (a);                                                      \
122                                                                         \
123   if (_sz < _want) {                                                    \
124     GROWBUF_SIZE(_sz, want, init, _gr);                                 \
125     if (_p) x_free(_a, _p);                                             \
126     _p = x_alloc(_a, _sz*_gr);                                          \
127     (buf) = _p; (sz) = _sz;                                             \
128   }                                                                     \
129 } while (0)
130
131 /*----- That's all, folks -------------------------------------------------*/
132
133 #ifdef __cplusplus
134   }
135 #endif
136
137 #endif