1 | /* |
2 | * Copyright © 2020 Benjamin Otte |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Authors: Benjamin Otte <otte@gnome.org> |
18 | */ |
19 | |
20 | #include <glib.h> |
21 | |
22 | G_BEGIN_DECLS |
23 | |
24 | #ifndef GDK_ARRAY_TYPE_NAME |
25 | #define GDK_ARRAY_TYPE_NAME GdkArray |
26 | #endif |
27 | |
28 | #ifndef GDK_ARRAY_NAME |
29 | #define GDK_ARRAY_NAME gdk_array |
30 | #endif |
31 | |
32 | #ifndef GDK_ARRAY_ELEMENT_TYPE |
33 | #define GDK_ARRAY_ELEMENT_TYPE gpointer |
34 | #endif |
35 | |
36 | #ifdef GDK_ARRAY_PREALLOC |
37 | #if GDK_ARRAY_PREALLOC == 0 |
38 | #undef GDK_ARRAY_PREALLOC |
39 | #endif |
40 | #endif |
41 | |
42 | #ifdef GDK_ARRAY_NULL_TERMINATED |
43 | #define GDK_ARRAY_REAL_SIZE(_size) ((_size) + 1) |
44 | #else |
45 | #define GDK_ARRAY_REAL_SIZE(_size) (_size) |
46 | #endif |
47 | |
48 | /* make this readable */ |
49 | #define _T_ GDK_ARRAY_ELEMENT_TYPE |
50 | #define GdkArray GDK_ARRAY_TYPE_NAME |
51 | #define gdk_array_paste_more(GDK_ARRAY_NAME, func_name) GDK_ARRAY_NAME ## _ ## func_name |
52 | #define gdk_array_paste(GDK_ARRAY_NAME, func_name) gdk_array_paste_more (GDK_ARRAY_NAME, func_name) |
53 | #define gdk_array(func_name) gdk_array_paste (GDK_ARRAY_NAME, func_name) |
54 | |
55 | typedef struct GdkArray GdkArray; |
56 | |
57 | struct GdkArray |
58 | { |
59 | _T_ *start; |
60 | _T_ *end; |
61 | _T_ *end_allocation; |
62 | #ifdef GDK_ARRAY_PREALLOC |
63 | _T_ preallocated[GDK_ARRAY_REAL_SIZE(GDK_ARRAY_PREALLOC)]; |
64 | #endif |
65 | }; |
66 | |
67 | /* no G_GNUC_UNUSED here, if you don't use an array type, remove it. */ |
68 | static inline void |
69 | gdk_array(init) (GdkArray *self) |
70 | { |
71 | #ifdef GDK_ARRAY_PREALLOC |
72 | self->start = self->preallocated; |
73 | self->end = self->start; |
74 | self->end_allocation = self->start + GDK_ARRAY_PREALLOC; |
75 | #ifdef GDK_ARRAY_NULL_TERMINATED |
76 | *self->start = *(_T_[1]) { 0 }; |
77 | #endif |
78 | #else |
79 | self->start = NULL; |
80 | self->end = NULL; |
81 | self->end_allocation = NULL; |
82 | #endif |
83 | } |
84 | |
85 | static inline void |
86 | gdk_array(free_elements) (_T_ *start, |
87 | _T_ *end) |
88 | { |
89 | #ifdef GDK_ARRAY_FREE_FUNC |
90 | _T_ *e; |
91 | for (e = start; e < end; e++) |
92 | #ifdef GDK_ARRAY_BY_VALUE |
93 | GDK_ARRAY_FREE_FUNC (accel: e); |
94 | #else |
95 | GDK_ARRAY_FREE_FUNC (*e); |
96 | #endif |
97 | #endif |
98 | } |
99 | |
100 | /* no G_GNUC_UNUSED here */ |
101 | static inline void |
102 | gdk_array(clear) (GdkArray *self) |
103 | { |
104 | gdk_array(free_elements) (start: self->start, end: self->end); |
105 | |
106 | #ifdef GDK_ARRAY_PREALLOC |
107 | if (self->start != self->preallocated) |
108 | #endif |
109 | g_free (mem: self->start); |
110 | gdk_array(init) (self); |
111 | } |
112 | |
113 | G_GNUC_UNUSED static inline _T_ * |
114 | gdk_array(get_data) (const GdkArray *self) |
115 | { |
116 | return self->start; |
117 | } |
118 | |
119 | G_GNUC_UNUSED static inline _T_ * |
120 | gdk_array(index) (const GdkArray *self, |
121 | gsize pos) |
122 | { |
123 | return self->start + pos; |
124 | } |
125 | |
126 | G_GNUC_UNUSED static inline gsize |
127 | gdk_array(get_capacity) (const GdkArray *self) |
128 | { |
129 | return self->end_allocation - self->start; |
130 | } |
131 | |
132 | G_GNUC_UNUSED static inline gsize |
133 | gdk_array(get_size) (const GdkArray *self) |
134 | { |
135 | return self->end - self->start; |
136 | } |
137 | |
138 | G_GNUC_UNUSED static inline gboolean |
139 | gdk_array(is_empty) (const GdkArray *self) |
140 | { |
141 | return self->end == self->start; |
142 | } |
143 | |
144 | G_GNUC_UNUSED static inline void |
145 | gdk_array(reserve) (GdkArray *self, |
146 | gsize n) |
147 | { |
148 | gsize new_size, size; |
149 | |
150 | if (n <= gdk_array(get_capacity) (self)) |
151 | return; |
152 | |
153 | size = gdk_array(get_size) (self); |
154 | new_size = 1 << g_bit_storage (MAX (GDK_ARRAY_REAL_SIZE (n), 16) - 1); |
155 | |
156 | #ifdef GDK_ARRAY_PREALLOC |
157 | if (self->start == self->preallocated) |
158 | { |
159 | self->start = g_new (_T_, new_size); |
160 | memcpy (dest: self->start, src: self->preallocated, n: sizeof (_T_) * GDK_ARRAY_REAL_SIZE (size)); |
161 | } |
162 | else |
163 | #endif |
164 | #ifdef GDK_ARRAY_NULL_TERMINATED |
165 | if (self->start == NULL) |
166 | { |
167 | self->start = g_new (_T_, new_size); |
168 | *self->start = *(_T_[1]) { 0 }; |
169 | } |
170 | else |
171 | #endif |
172 | self->start = g_renew (_T_, self->start, new_size); |
173 | |
174 | self->end = self->start + size; |
175 | self->end_allocation = self->start + new_size; |
176 | #ifdef GDK_ARRAY_NULL_TERMINATED |
177 | self->end_allocation--; |
178 | #endif |
179 | } |
180 | |
181 | G_GNUC_UNUSED static inline void |
182 | gdk_array(splice) (GdkArray *self, |
183 | gsize pos, |
184 | gsize removed, |
185 | gboolean stolen, |
186 | _T_ *additions, |
187 | gsize added) |
188 | { |
189 | gsize size; |
190 | gsize remaining; |
191 | |
192 | size = gdk_array(get_size) (self); |
193 | g_assert (pos + removed <= size); |
194 | remaining = size - pos - removed; |
195 | |
196 | if (!stolen) |
197 | gdk_array(free_elements) (gdk_array(index) (self, pos), |
198 | gdk_array(index) (self, pos: pos + removed)); |
199 | |
200 | gdk_array(reserve) (self, n: size - removed + added); |
201 | |
202 | if (GDK_ARRAY_REAL_SIZE (remaining) && removed != added) |
203 | memmove (gdk_array(index) (self, pos: pos + added), |
204 | gdk_array(index) (self, pos: pos + removed), |
205 | GDK_ARRAY_REAL_SIZE (remaining) * sizeof (_T_)); |
206 | |
207 | if (added) |
208 | { |
209 | if (additions) |
210 | memcpy (gdk_array(index) (self, pos), |
211 | src: additions, |
212 | n: added * sizeof (_T_)); |
213 | #ifndef GDK_ARRAY_NO_MEMSET |
214 | else |
215 | memset (gdk_array(index) (self, pos), c: 0, n: added * sizeof (_T_)); |
216 | #endif |
217 | } |
218 | |
219 | |
220 | /* might overflow, but does the right thing */ |
221 | self->end += added - removed; |
222 | } |
223 | |
224 | G_GNUC_UNUSED static void |
225 | gdk_array(set_size) (GdkArray *self, |
226 | gsize new_size) |
227 | { |
228 | gsize old_size = gdk_array(get_size) (self); |
229 | if (new_size > old_size) |
230 | gdk_array(splice) (self, pos: old_size, removed: 0, FALSE, NULL, added: new_size - old_size); |
231 | else |
232 | gdk_array(splice) (self, pos: new_size, removed: old_size - new_size, FALSE, NULL, added: 0); |
233 | } |
234 | |
235 | G_GNUC_UNUSED static void |
236 | gdk_array(append) (GdkArray *self, |
237 | #ifdef GDK_ARRAY_BY_VALUE |
238 | _T_ *value) |
239 | #else |
240 | _T_ value) |
241 | #endif |
242 | { |
243 | gdk_array(splice) (self, |
244 | gdk_array(get_size) (self), |
245 | removed: 0, |
246 | FALSE, |
247 | #ifdef GDK_ARRAY_BY_VALUE |
248 | additions: value, |
249 | #else |
250 | &value, |
251 | #endif |
252 | added: 1); |
253 | } |
254 | |
255 | #ifdef GDK_ARRAY_BY_VALUE |
256 | G_GNUC_UNUSED static _T_ * |
257 | gdk_array(get) (const GdkArray *self, |
258 | gsize pos) |
259 | { |
260 | return gdk_array(index) (self, pos); |
261 | } |
262 | #else |
263 | G_GNUC_UNUSED static _T_ |
264 | gdk_array(get) (const GdkArray *self, |
265 | gsize pos) |
266 | { |
267 | return *gdk_array(index) (self, pos); |
268 | } |
269 | #endif |
270 | |
271 | #ifndef GDK_ARRAY_NO_UNDEF |
272 | |
273 | #undef _T_ |
274 | #undef GdkArray |
275 | #undef gdk_array_paste_more |
276 | #undef gdk_array_paste |
277 | #undef gdk_array |
278 | #undef GDK_ARRAY_REAL_SIZE |
279 | |
280 | #undef GDK_ARRAY_BY_VALUE |
281 | #undef GDK_ARRAY_ELEMENT_TYPE |
282 | #undef GDK_ARRAY_FREE_FUNC |
283 | #undef GDK_ARRAY_NAME |
284 | #undef GDK_ARRAY_NULL_TERMINATED |
285 | #undef GDK_ARRAY_PREALLOC |
286 | #undef GDK_ARRAY_TYPE_NAME |
287 | #undef GDK_ARRAY_NO_MEMSET |
288 | #endif |
289 | |