1 /* String descriptors.
2 Copyright (C) 2023 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation, either version 3 of the
7 License, or (at your option) any later version.
8
9 This file 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
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2023. */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #define GL_STRING_DESC_INLINE _GL_EXTERN_INLINE
24
25 /* Specification and inline definitions. */
26 #include "string-desc.h"
27
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "ialloc.h"
33 #include "full-write.h"
34
35
36 /* ==== Side-effect-free operations on string descriptors ==== */
37
38 /* Return true if A and B are equal. */
39 bool
40 string_desc_equals (string_desc_t a, string_desc_t b)
41 {
42 return (a._nbytes == b._nbytes
43 && (a._nbytes == 0 || memcmp (a._data, b._data, a._nbytes) == 0));
44 }
45
46 bool
47 string_desc_startswith (string_desc_t s, string_desc_t prefix)
48 {
49 return (s._nbytes >= prefix._nbytes
50 && (prefix._nbytes == 0
51 || memcmp (s._data, prefix._data, prefix._nbytes) == 0));
52 }
53
54 bool
55 string_desc_endswith (string_desc_t s, string_desc_t suffix)
56 {
57 return (s._nbytes >= suffix._nbytes
58 && (suffix._nbytes == 0
59 || memcmp (s._data + (s._nbytes - suffix._nbytes), suffix._data,
60 suffix._nbytes) == 0));
61 }
62
63 int
64 string_desc_cmp (string_desc_t a, string_desc_t b)
65 {
66 if (a._nbytes > b._nbytes)
67 {
68 if (b._nbytes == 0)
69 return 1;
70 return (memcmp (a._data, b._data, b._nbytes) < 0 ? -1 : 1);
71 }
72 else if (a._nbytes < b._nbytes)
73 {
74 if (a._nbytes == 0)
75 return -1;
76 return (memcmp (a._data, b._data, a._nbytes) > 0 ? 1 : -1);
77 }
78 else /* a._nbytes == b._nbytes */
79 {
80 if (a._nbytes == 0)
81 return 0;
82 return memcmp (a._data, b._data, a._nbytes);
83 }
84 }
85
86 ptrdiff_t
87 string_desc_index (string_desc_t s, char c)
88 {
89 if (s._nbytes > 0)
90 {
91 void *found = memchr (s._data, (unsigned char) c, s._nbytes);
92 if (found != NULL)
93 return (char *) found - s._data;
94 }
95 return -1;
96 }
97
98 ptrdiff_t
99 string_desc_last_index (string_desc_t s, char c)
100 {
101 if (s._nbytes > 0)
102 {
103 void *found = memrchr (s._data, (unsigned char) c, s._nbytes);
104 if (found != NULL)
105 return (char *) found - s._data;
106 }
107 return -1;
108 }
109
110 string_desc_t
111 string_desc_new_empty (void)
112 {
113 string_desc_t result;
114
115 result._nbytes = 0;
116 result._data = NULL;
117
118 return result;
119
120 }
121
122 string_desc_t
123 string_desc_from_c (const char *s)
124 {
125 string_desc_t result;
126
127 result._nbytes = strlen (s);
128 result._data = (char *) s;
129
130 return result;
131 }
132
133 string_desc_t
134 string_desc_substring (string_desc_t s, idx_t start, idx_t end)
135 {
136 string_desc_t result;
137
138 if (!(start >= 0 && start <= end))
139 /* Invalid arguments. */
140 abort ();
141
142 result._nbytes = end - start;
143 result._data = s._data + start;
144
145 return result;
146 }
147
148 int
149 string_desc_write (int fd, string_desc_t s)
150 {
151 if (s._nbytes > 0)
152 if (full_write (fd, s._data, s._nbytes) != s._nbytes)
153 /* errno is set here. */
154 return -1;
155 return 0;
156 }
157
158 int
159 string_desc_fwrite (FILE *fp, string_desc_t s)
160 {
161 if (s._nbytes > 0)
162 if (fwrite (s._data, 1, s._nbytes, fp) != s._nbytes)
163 return -1;
164 return 0;
165 }
166
167
168 /* ==== Memory-allocating operations on string descriptors ==== */
169
170 int
171 string_desc_new (string_desc_t *resultp, idx_t n)
172 {
173 string_desc_t result;
174
175 if (!(n >= 0))
176 /* Invalid argument. */
177 abort ();
178
179 result._nbytes = n;
180 if (n == 0)
181 result._data = NULL;
182 else
183 {
184 result._data = (char *) imalloc (n);
185 if (result._data == NULL)
186 /* errno is set here. */
187 return -1;
188 }
189
190 *resultp = result;
191 return 0;
192 }
193
194 string_desc_t
195 string_desc_new_addr (idx_t n, char *addr)
196 {
197 string_desc_t result;
198
199 result._nbytes = n;
200 if (n == 0)
201 result._data = NULL;
202 else
203 result._data = addr;
204
205 return result;
206 }
207
208 int
209 string_desc_new_filled (string_desc_t *resultp, idx_t n, char c)
210 {
211 string_desc_t result;
212
213 result._nbytes = n;
214 if (n == 0)
215 result._data = NULL;
216 else
217 {
218 result._data = (char *) imalloc (n);
219 if (result._data == NULL)
220 /* errno is set here. */
221 return -1;
222 memset (result._data, (unsigned char) c, n);
223 }
224
225 *resultp = result;
226 return 0;
227 }
228
229 int
230 string_desc_copy (string_desc_t *resultp, string_desc_t s)
231 {
232 string_desc_t result;
233 idx_t n = s._nbytes;
234
235 result._nbytes = n;
236 if (n == 0)
237 result._data = NULL;
238 else
239 {
240 result._data = (char *) imalloc (n);
241 if (result._data == NULL)
242 /* errno is set here. */
243 return -1;
244 memcpy (result._data, s._data, n);
245 }
246
247 *resultp = result;
248 return 0;
249 }
250
251 int
252 string_desc_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...)
253 {
254 if (n <= 0)
255 /* Invalid argument. */
256 abort ();
257
258 idx_t total = 0;
259 total += string1._nbytes;
260 if (n > 1)
261 {
262 va_list other_strings;
263 idx_t i;
264
265 va_start (other_strings, string1);
266 for (i = n - 1; i > 0; i--)
267 {
268 string_desc_t arg = va_arg (other_strings, string_desc_t);
269 total += arg._nbytes;
270 }
271 va_end (other_strings);
272 }
273
274 char *combined = (char *) imalloc (total);
275 if (combined == NULL)
276 /* errno is set here. */
277 return -1;
278 idx_t pos = 0;
279 memcpy (combined, string1._data, string1._nbytes);
280 pos += string1._nbytes;
281 if (n > 1)
282 {
283 va_list other_strings;
284 idx_t i;
285
286 va_start (other_strings, string1);
287 for (i = n - 1; i > 0; i--)
288 {
289 string_desc_t arg = va_arg (other_strings, string_desc_t);
290 if (arg._nbytes > 0)
291 memcpy (combined + pos, arg._data, arg._nbytes);
292 pos += arg._nbytes;
293 }
294 va_end (other_strings);
295 }
296
297 string_desc_t result;
298 result._nbytes = total;
299 result._data = combined;
300
301 *resultp = result;
302 return 0;
303 }
304
305 char *
306 string_desc_c (string_desc_t s)
307 {
308 idx_t n = s._nbytes;
309 char *result = (char *) imalloc (n + 1);
310 if (result == NULL)
311 /* errno is set here. */
312 return NULL;
313 if (n > 0)
314 memcpy (result, s._data, n);
315 result[n] = '\0';
316
317 return result;
318 }
319
320
321 /* ==== Operations with side effects on string descriptors ==== */
322
323 void
324 string_desc_set_char_at (string_desc_t s, idx_t i, char c)
325 {
326 if (!(i >= 0 && i < s._nbytes))
327 /* Invalid argument. */
328 abort ();
329 s._data[i] = c;
330 }
331
332 void
333 string_desc_fill (string_desc_t s, idx_t start, idx_t end, char c)
334 {
335 if (!(start >= 0 && start <= end))
336 /* Invalid arguments. */
337 abort ();
338
339 if (start < end)
340 memset (s._data + start, (unsigned char) c, end - start);
341 }
342
343 void
344 string_desc_overwrite (string_desc_t s, idx_t start, string_desc_t t)
345 {
346 if (!(start >= 0 && start + t._nbytes <= s._nbytes))
347 /* Invalid arguments. */
348 abort ();
349
350 if (t._nbytes > 0)
351 memcpy (s._data + start, t._data, t._nbytes);
352 }
353
354 void
355 string_desc_free (string_desc_t s)
356 {
357 free (s._data);
358 }