1 /* constructor-helpers.c - Helper library for the constructor test
2 *
3 * Copyright © 2023 Luca Bacci
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /* This helper library manages a set of strings. Strings can be added,
22 * removed and checked for presence. This library does not call into
23 * libc, so can be used from module constructors in a safe manner on
24 * a wide range of operating systems.
25 *
26 * GLib is used only for assertions or hard errors; in such cases we
27 * don't really care about supported libc calls, as the test ought to
28 * fail anyway.
29 */
30
31 #include <stddef.h> /* for size_t */
32
33 #include <glib.h>
34
35 #if defined (_MSC_VER)
36 # pragma optimize ("", off)
37 #else
38 # if defined (__clang__)
39 # pragma clang optimize off
40 # elif defined (__GNUC__)
41 # pragma GCC optimize ("O0")
42 # endif
43 #endif
44
45 #if defined(_WIN32)
46 #define MODULE_EXPORT \
47 __declspec (dllexport)
48 #elif defined (__GNUC__)
49 #define MODULE_EXPORT \
50 __attribute__((visibility("default")))
51 #else
52 #define MODULE_EXPORT
53 #endif
54
55 char buffer[500];
56 size_t position;
57
58 MODULE_EXPORT
59 void string_add (const char *string);
60
61 MODULE_EXPORT
62 void string_add_exclusive (const char *string);
63
64 MODULE_EXPORT
65 int string_remove (const char *string);
66
67 MODULE_EXPORT
68 int string_find (const char *string);
69
70 MODULE_EXPORT
71 void string_check (const char *string);
72
73 static size_t
74 strlen_ (const char *string)
75 {
76 size_t i = 0;
77
78 g_assert_nonnull (string);
79
80 while (string[i] != 0)
81 i++;
82
83 return i;
84 }
85
86 static void
87 memmove_ (char *buf,
88 size_t from,
89 size_t size,
90 size_t to)
91 {
92 g_assert_true (to <= from);
93
94 for (size_t i = 0; i < size; i++)
95 buffer[to + i] = buffer[from + i];
96 }
97
98 static void
99 memcpy_ (char *dst,
100 const char *src,
101 size_t size)
102 {
103 for (size_t i = 0; i < size; i++)
104 dst[i] = src[i];
105 }
106
107 static void
108 memset_ (char *dst,
109 char val,
110 size_t size)
111 {
112 for (size_t i = 0; i < size; i++)
113 dst[i] = val;
114 }
115
116 static size_t
117 string_find_index_ (const char *string)
118 {
119 size_t string_len;
120 size_t i = 0;
121
122 g_assert_nonnull (string);
123 g_assert_true ((string_len = strlen_ (string)) > 0);
124
125 for (i = 0; (i < sizeof (buffer) - position) && (buffer[i] != 0);)
126 {
127 const char *iter = &buffer[i];
128 size_t len = strlen_ (iter);
129
130 if (len == string_len && strcmp (iter, string) == 0)
131 return i;
132
133 i += len + 1;
134 }
135
136 return sizeof (buffer);
137 }
138
139 /**< private >
140 * string_add:
141 *
142 * @string: NULL-terminated string. Must not be empty
143 *
144 * Adds @string to the set
145 */
146 MODULE_EXPORT
147 void
148 string_add (const char *string)
149 {
150 size_t len, size;
151
152 g_assert_nonnull (string);
153 g_assert_true ((len = strlen_ (string)) > 0);
154
155 size = len + 1;
156
157 if (size > sizeof (buffer) - position)
158 g_error ("Not enough space in the buffer");
159
160 memcpy_ (buffer + position, string, size);
161
162 position += size;
163 }
164
165 /**< private >
166 * string_add_exclusive:
167 *
168 * @string: NULL-terminated string. Must not be empty
169 *
170 * Adds @string to the set, asserting that it's not already present.
171 */
172 MODULE_EXPORT
173 void
174 string_add_exclusive (const char *string)
175 {
176 if (string_find_index_ (string) < sizeof (buffer))
177 g_error ("string %s already set", string);
178
179 string_add (string);
180 }
181
182 /**< private >
183 * string_remove:
184 *
185 * @string: NULL-terminated string. Must not be empty
186 *
187 * Removes the first occurrence of @string from the set.
188 *
189 * Returns: 1 if the string was removed, 0 otherwise.
190 */
191 MODULE_EXPORT
192 int
193 string_remove (const char *string)
194 {
195 size_t index = string_find_index_ (string);
196 size_t len, size;
197
198 if (index >= sizeof (buffer))
199 return 0;
200
201 g_assert_true ((len = strlen_ (string)) > 0);
202 size = len + 1;
203
204 memmove_ (buffer, index + size, index, position - (index + size));
205
206 position -= size;
207
208 memset_ (buffer + position, 0, size);
209
210 return 1;
211 }
212
213 /**< private >
214 * string_find:
215 *
216 * @string: NULL-terminated string. Must not be empty
217 *
218 * Returns 1 if the string is present, 0 otherwise
219 */
220 MODULE_EXPORT
221 int string_find (const char *string)
222 {
223 return string_find_index_ (string) < sizeof (buffer);
224 }
225
226 /**< private >
227 * string_check:
228 *
229 * @string: NULL-terminated string. Must not be empty
230 *
231 * Asserts that @string is present in the set
232 */
233 MODULE_EXPORT
234 void
235 string_check (const char *string)
236 {
237 if (string_find_index_ (string) >= sizeof (buffer))
238 g_error ("String %s not present", string);
239 }