/src/gettext/gettext-tools/libgettextpo/string-desc.h
Line | Count | Source |
1 | | /* String descriptors. |
2 | | Copyright (C) 2023-2026 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 | | #ifndef _STRING_DESC_H |
20 | | #define _STRING_DESC_H 1 |
21 | | |
22 | | /* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE, |
23 | | _GL_ATTRIBUTE_NODISCARD, _GL_ATTRIBUTE_NONNULL, |
24 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO. */ |
25 | | #if !_GL_CONFIG_H_INCLUDED |
26 | | #error "Please include config.h first." |
27 | | #endif |
28 | | |
29 | | /* Get ptrdiff_t. */ |
30 | | #include <stddef.h> |
31 | | |
32 | | /* Get FILE. */ |
33 | | #include <stdio.h> |
34 | | |
35 | | /* Get abort(), free(). */ |
36 | | #include <stdlib.h> |
37 | | |
38 | | /* Get idx_t. */ |
39 | | #include "idx.h" |
40 | | |
41 | | /* Whether the compiler supports statement-expressions. |
42 | | Test program: |
43 | | int f (int x) { return ({ x; x; }); } |
44 | | */ |
45 | | #if defined __GNUC__ /* both C and C++ mode */ \ |
46 | | || defined __clang__ /* both C and C++ mode */ \ |
47 | | || (defined __SUNPRO_C && __SUNPRO_C >= 0x5150) /* C mode */ \ |
48 | | || (defined __SUNPRO_CC && __SUNPRO_CC >= 0x5150) /* C++ mode */ |
49 | | # define HAVE_STATEMENT_EXPRESSIONS 1 |
50 | | #endif |
51 | | |
52 | | /* Whether the compiler supports _Generic. |
53 | | Test program: |
54 | | int f (int x) { return _Generic (x, char *: 2, int: 3); } |
55 | | */ |
56 | | #if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 9) > 4 && !defined __cplusplus) /* C mode */ \ |
57 | | || (defined __clang__ && __clang_major__ >= 3) /* both C and C++ mode */ \ |
58 | | || (defined __SUNPRO_C && __SUNPRO_C >= 0x5150) /* C mode */ \ |
59 | | || (__STDC_VERSION__ >= 201112L && !defined __GNUC__) /* C mode */ |
60 | | # define HAVE__GENERIC 1 |
61 | | #endif |
62 | | |
63 | | /* Whether the compiler supports typeof. |
64 | | Test program: |
65 | | int f (int x) { typeof (x) y = x; return y; } |
66 | | */ |
67 | | #if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 1) > 3) /* both C and C++ mode */ \ |
68 | | || (defined __clang__ && __clang_major__ >= 3 /* both C and C++ mode */ \ |
69 | | && !(defined __cplusplus && !defined __GNUC__)) /* except for clang-cl in C++ mode */ \ |
70 | | || (defined __SUNPRO_C && __SUNPRO_C >= 0x5110) /* C mode */ \ |
71 | | || __STDC_VERSION__ >= 202311L /* C mode */ |
72 | | # define HAVE_TYPEOF 1 |
73 | | #endif |
74 | | |
75 | | /* Whether the compiler supports __builtin_choose_expr. |
76 | | _Generic and __builtin_choose_expr are like conditional expressions, |
77 | | except that the return types of the branches need not match: They avoid an |
78 | | "error: type mismatch in conditional expression". |
79 | | Test program: |
80 | | int f (int x) { return __builtin_choose_expr (sizeof (x) == 4, 2, 3); } |
81 | | */ |
82 | | #if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 1) > 3) /* both C and C++ mode */ \ |
83 | | || (defined __clang__ && __clang_major__ >= 3) /* both C and C++ mode */ |
84 | | # define HAVE_BUILTIN_CHOOSE_EXPR 1 |
85 | | #endif |
86 | | |
87 | | /* Whether the compiler supports __builtin_constant_p and whether that built-in |
88 | | returns true for string literals. |
89 | | _Generic has an antiquated treatment of string literals: It treats string |
90 | | literals like 'char *', not 'const char *'. To compensate for this, we need |
91 | | __builtin_constant_p. |
92 | | Test program: |
93 | | int i, v[__builtin_constant_p ("x") > __builtin_constant_p (i) ? 1 : -1]; |
94 | | */ |
95 | | #if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 1) > 3) /* both C and C++ mode */ \ |
96 | | || (defined __clang__ && __clang_major__ >= 4) /* both C and C++ mode */ |
97 | | # define HAVE_BUILTIN_CONSTANT_P 1 |
98 | | #endif |
99 | | |
100 | | /* Whether we support rw_string_desc_t as distinct from string_desc_t. */ |
101 | | #if HAVE_STATEMENT_EXPRESSIONS && HAVE__GENERIC && HAVE_TYPEOF \ |
102 | | && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_CONSTANT_P |
103 | | # define HAVE_RW_STRING_DESC 1 |
104 | | #endif |
105 | | |
106 | | _GL_INLINE_HEADER_BEGIN |
107 | | #ifndef GL_STRING_DESC_INLINE |
108 | | # define GL_STRING_DESC_INLINE _GL_INLINE |
109 | | #endif |
110 | | |
111 | | #ifdef __cplusplus |
112 | | extern "C" { |
113 | | #endif |
114 | | |
115 | | |
116 | | /* Type describing a string that may contain NUL bytes. |
117 | | It's merely a descriptor of an array of bytes. |
118 | | It comes in two flavours: |
119 | | - string_desc_t is read-only, |
120 | | - rw_string_desc_t is read-write. |
121 | | When we write |
122 | | [rw_]string_desc_t |
123 | | it means either string_desc_t or rw_string_desc_t. */ |
124 | | typedef struct rw_string_desc_t rw_string_desc_t; |
125 | | struct rw_string_desc_t |
126 | | { |
127 | | /* The fields of this struct should be considered private. */ |
128 | | idx_t _nbytes; |
129 | | char *_data; |
130 | | }; |
131 | | #if HAVE_RW_STRING_DESC |
132 | | typedef struct string_desc_t string_desc_t; |
133 | | struct string_desc_t |
134 | | { |
135 | | /* The fields of this struct should be considered private. */ |
136 | | idx_t _nbytes; |
137 | | const char *_data; |
138 | | }; |
139 | | #else |
140 | | typedef rw_string_desc_t string_desc_t; |
141 | | #endif |
142 | | |
143 | | /* String descriptors can be passed and returned by value. |
144 | | |
145 | | String descriptors and NUL-terminated 'const char *'/'char *' C strings |
146 | | cannot be used interchangeably. You will get compilation errors if you |
147 | | attempt to assign a string descriptor to a C string or vice versa. */ |
148 | | |
149 | | |
150 | | /* ==== Conversions between string descriptors ==== */ |
151 | | |
152 | | /* Return a read-only view of S. */ |
153 | | #if 0 /* Defined inline below. */ |
154 | | extern string_desc_t sd_readonly (rw_string_desc_t s); |
155 | | #endif |
156 | | |
157 | | /* Return a read-only view of S. |
158 | | Attention: This function is as dangerous as a cast from 'const char *' |
159 | | to 'char *'! */ |
160 | | #if 0 /* Defined inline below. */ |
161 | | extern rw_string_desc_t sd_readwrite (string_desc_t s); |
162 | | #endif |
163 | | |
164 | | |
165 | | /* ==== Side-effect-free operations on string descriptors ==== */ |
166 | | |
167 | | /* Return the length of the string S. */ |
168 | | #if 0 /* Defined inline below. */ |
169 | | extern idx_t sd_length ([rw_]string_desc_t s); |
170 | | #endif |
171 | | |
172 | | /* Return the byte at index I of string S. |
173 | | I must be < length(S). */ |
174 | | #if 0 /* Defined inline below. */ |
175 | | extern char sd_char_at ([rw_]string_desc_t s, idx_t i); |
176 | | #endif |
177 | | |
178 | | /* Return a read-only view of the bytes of S. */ |
179 | | #if 0 /* Defined inline below. */ |
180 | | extern const char * sd_data (string_desc_t s); |
181 | | extern char * sd_data (rw_string_desc_t s); |
182 | | #endif |
183 | | |
184 | | /* Return true if S is the empty string. */ |
185 | | #if 0 /* Defined inline below. */ |
186 | | extern bool sd_is_empty ([rw_]string_desc_t s); |
187 | | #endif |
188 | | |
189 | | /* Return true if A and B are equal. */ |
190 | | #if 0 /* Defined inline below. */ |
191 | | extern bool sd_equals ([rw_]string_desc_t a, [rw_]string_desc_t b); |
192 | | #endif |
193 | | |
194 | | /* Return true if S starts with PREFIX. */ |
195 | | #if 0 /* Defined inline below. */ |
196 | | extern bool sd_startswith ([rw_]string_desc_t s, [rw_]string_desc_t prefix); |
197 | | #endif |
198 | | |
199 | | /* Return true if S ends with SUFFIX. */ |
200 | | #if 0 /* Defined inline below. */ |
201 | | extern bool sd_endswith ([rw_]string_desc_t s, [rw_]string_desc_t suffix); |
202 | | #endif |
203 | | |
204 | | /* Return > 0, == 0, or < 0 if A > B, A == B, A < B. |
205 | | This uses a lexicographic ordering, where the bytes are compared as |
206 | | 'unsigned char'. */ |
207 | | #if 0 /* Defined inline below. */ |
208 | | extern int sd_cmp ([rw_]string_desc_t a, [rw_]string_desc_t b); |
209 | | #endif |
210 | | |
211 | | /* Return > 0, == 0, or < 0 if A > B, A == B, A < B. |
212 | | Either A or B must be entirely ASCII. |
213 | | This uses a lexicographic ordering, where the bytes are compared as |
214 | | 'unsigned char', ignoring case, in the "C" locale. */ |
215 | | #if 0 /* Defined inline below. */ |
216 | | extern int sd_c_casecmp ([rw_]string_desc_t a, [rw_]string_desc_t b); |
217 | | #endif |
218 | | |
219 | | /* Return the index of the first occurrence of C in S, |
220 | | or -1 if there is none. */ |
221 | | #if 0 /* Defined inline below. */ |
222 | | extern ptrdiff_t sd_index ([rw_]string_desc_t s, char c); |
223 | | #endif |
224 | | |
225 | | /* Return the index of the last occurrence of C in S, |
226 | | or -1 if there is none. */ |
227 | | #if 0 /* Defined inline below. */ |
228 | | extern ptrdiff_t sd_last_index ([rw_]string_desc_t s, char c); |
229 | | #endif |
230 | | |
231 | | /* Return the index of the first occurrence of NEEDLE in HAYSTACK, |
232 | | or -1 if there is none. */ |
233 | | #if 0 /* Defined inline below. */ |
234 | | extern ptrdiff_t sd_contains ([rw_]string_desc_t haystack, [rw_]string_desc_t needle); |
235 | | #endif |
236 | | |
237 | | /* Return an empty string. */ |
238 | | extern string_desc_t sd_new_empty (void); |
239 | | |
240 | | /* Construct and return a string of length N, at the given memory address. */ |
241 | | #if 0 /* Defined inline below. */ |
242 | | extern string_desc_t sd_new_addr (idx_t n, const char *addr) |
243 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1); |
244 | | extern rw_string_desc_t sd_new_addr (idx_t n, char *addr) |
245 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1); |
246 | | #endif |
247 | | |
248 | | /* Return a string that represents the C string S, of length strlen (S). */ |
249 | | extern string_desc_t sd_from_c (const char *s); |
250 | | |
251 | | /* Return the substring of S, starting at offset START and ending at offset END. |
252 | | START must be <= END. |
253 | | The result is of length END - START. |
254 | | The result must not be freed (since its storage is part of the storage |
255 | | of S). */ |
256 | | #if 0 /* Defined inline below. */ |
257 | | extern string_desc_t sd_substring (string_desc_t s, idx_t start, idx_t end); |
258 | | extern rw_string_desc_t sd_substring (rw_string_desc_t s, idx_t start, idx_t end); |
259 | | #endif |
260 | | |
261 | | /* Output S to the file descriptor FD. |
262 | | Return 0 if successful. |
263 | | Upon error, return -1 with errno set. */ |
264 | | #if 0 /* Defined inline below. */ |
265 | | extern int sd_write (int fd, [rw_]string_desc_t s); |
266 | | #endif |
267 | | |
268 | | /* Output S to the FILE stream FP. |
269 | | Return 0 if successful. |
270 | | Upon error, return -1. */ |
271 | | #if 0 /* Defined inline below. */ |
272 | | extern int sd_fwrite (FILE *fp, [rw_]string_desc_t s); |
273 | | #endif |
274 | | |
275 | | |
276 | | /* ==== Memory-allocating operations on string descriptors ==== */ |
277 | | |
278 | | /* Construct a string of length N, with uninitialized contents. |
279 | | Return 0 if successful. |
280 | | Upon error, return -1 with errno set. */ |
281 | | _GL_ATTRIBUTE_NODISCARD |
282 | | extern int sd_new (rw_string_desc_t *resultp, idx_t n); |
283 | | |
284 | | /* Construct a string of length N, filled with C. |
285 | | Return 0 if successful. |
286 | | Upon error, return -1 with errno set. */ |
287 | | _GL_ATTRIBUTE_NODISCARD |
288 | | extern int sd_new_filled (rw_string_desc_t *resultp, idx_t n, char c); |
289 | | |
290 | | /* Construct a copy of string S. |
291 | | Return 0 if successful. |
292 | | Upon error, return -1 with errno set. */ |
293 | | #if 0 /* Defined inline below. */ |
294 | | _GL_ATTRIBUTE_NODISCARD |
295 | | extern int sd_copy (rw_string_desc_t *resultp, [rw_]string_desc_t s); |
296 | | #endif |
297 | | |
298 | | /* Construct the concatenation of N strings. N must be > 0. |
299 | | Return 0 if successful. |
300 | | Upon error, return -1 with errno set. */ |
301 | | _GL_ATTRIBUTE_NODISCARD |
302 | | extern int sd_concat (rw_string_desc_t *resultp, idx_t n, /* [rw_]string_desc_t string1, */ ...); |
303 | | |
304 | | /* Construct a copy of string S, as a NUL-terminated C string. |
305 | | Return it is successful. |
306 | | Upon error, return NULL with errno set. */ |
307 | | #if 0 /* Defined inline below. */ |
308 | | extern char * sd_c ([rw_]string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE; |
309 | | #endif |
310 | | |
311 | | |
312 | | /* ==== Operations with side effects on string descriptors ==== */ |
313 | | |
314 | | /* Overwrite the byte at index I of string S with C. |
315 | | I must be < length(S). */ |
316 | | extern void sd_set_char_at (rw_string_desc_t s, idx_t i, char c); |
317 | | |
318 | | /* Fill part of S, starting at offset START and ending at offset END, |
319 | | with copies of C. |
320 | | START must be <= END. */ |
321 | | extern void sd_fill (rw_string_desc_t s, idx_t start, idx_t end, char c); |
322 | | |
323 | | /* Overwrite part of S with T, starting at offset START. |
324 | | START + length(T) must be <= length (S). */ |
325 | | #if 0 /* Defined inline below. */ |
326 | | extern void sd_overwrite (rw_string_desc_t s, idx_t start, [rw_]string_desc_t t); |
327 | | #endif |
328 | | |
329 | | /* Free S. */ |
330 | | extern void sd_free (rw_string_desc_t s); |
331 | | |
332 | | |
333 | | /* ==== Inline function definitions ==== */ |
334 | | |
335 | | #if HAVE_RW_STRING_DESC |
336 | | GL_STRING_DESC_INLINE string_desc_t |
337 | | sd_readonly (rw_string_desc_t s) |
338 | 0 | { |
339 | 0 | string_desc_t result; |
340 | 0 | result._nbytes = s._nbytes; |
341 | 0 | result._data = s._data; |
342 | 0 | return result; |
343 | 0 | } |
344 | | #else |
345 | | # define sd_readonly(s) (s) |
346 | | #endif |
347 | | |
348 | | #if HAVE_RW_STRING_DESC |
349 | | GL_STRING_DESC_INLINE rw_string_desc_t |
350 | | sd_readwrite (string_desc_t s) |
351 | 0 | { |
352 | 0 | rw_string_desc_t result; |
353 | 0 | result._nbytes = s._nbytes; |
354 | 0 | result._data = (char *) s._data; |
355 | 0 | return result; |
356 | 0 | } |
357 | | #else |
358 | | # define sd_readwrite(s) (s) |
359 | | #endif |
360 | | |
361 | | #if HAVE_RW_STRING_DESC |
362 | | # define sd_length(s) \ |
363 | 0 | _Generic (s, \ |
364 | 0 | string_desc_t : (s)._nbytes, \ |
365 | 0 | rw_string_desc_t : (s)._nbytes) |
366 | | #else |
367 | | GL_STRING_DESC_INLINE idx_t |
368 | | sd_length (string_desc_t s) |
369 | | { |
370 | | return s._nbytes; |
371 | | } |
372 | | #endif |
373 | | |
374 | | #if HAVE_RW_STRING_DESC |
375 | | # define sd_char_at(s, i) \ |
376 | 0 | ({typeof (s) _s_ = (s); \ |
377 | 0 | _sd_char_at (_s_._nbytes, _s_._data, i); \ |
378 | 0 | }) |
379 | | GL_STRING_DESC_INLINE char |
380 | | _sd_char_at (idx_t s_nbytes, const char *s_data, idx_t i) |
381 | | _GL_ATTRIBUTE_NONNULL ((2)); |
382 | | GL_STRING_DESC_INLINE char |
383 | | _sd_char_at (idx_t s_nbytes, const char *s_data, idx_t i) |
384 | 0 | { |
385 | 0 | if (!(i >= 0 && i < s_nbytes)) |
386 | | /* Invalid argument. */ |
387 | 0 | abort (); |
388 | 0 | return s_data[i]; |
389 | 0 | } |
390 | | #else |
391 | | GL_STRING_DESC_INLINE char |
392 | | sd_char_at (string_desc_t s, idx_t i) |
393 | | { |
394 | | if (!(i >= 0 && i < s._nbytes)) |
395 | | /* Invalid argument. */ |
396 | | abort (); |
397 | | return s._data[i]; |
398 | | } |
399 | | #endif |
400 | | |
401 | | #if HAVE_RW_STRING_DESC |
402 | | # define sd_data(s) \ |
403 | | _Generic (s, \ |
404 | | string_desc_t : (s)._data, \ |
405 | | rw_string_desc_t : (s)._data) |
406 | | #else |
407 | | GL_STRING_DESC_INLINE const char * |
408 | | sd_data (string_desc_t s) |
409 | | { |
410 | | return s._data; |
411 | | } |
412 | | #endif |
413 | | |
414 | | #if HAVE_RW_STRING_DESC |
415 | | # define sd_is_empty(s) \ |
416 | | _Generic (s, \ |
417 | | string_desc_t : (s)._nbytes == 0, \ |
418 | | rw_string_desc_t : (s)._nbytes == 0) |
419 | | #else |
420 | | GL_STRING_DESC_INLINE bool |
421 | | sd_is_empty (string_desc_t s) |
422 | | { |
423 | | return s._nbytes == 0; |
424 | | } |
425 | | #endif |
426 | | |
427 | | extern bool _sd_equals (idx_t a_nbytes, const char *a_data, |
428 | | idx_t b_nbytes, const char *b_data) |
429 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1) |
430 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3); |
431 | | #if HAVE_RW_STRING_DESC |
432 | | # define sd_equals(a, b) \ |
433 | | ({typeof (a) _a_ = (a); \ |
434 | | typeof (b) _b_ = (b); \ |
435 | | _sd_equals (_a_._nbytes, _a_._data, _b_._nbytes, _b_._data); \ |
436 | | }) |
437 | | #else |
438 | | GL_STRING_DESC_INLINE bool |
439 | | sd_equals (string_desc_t a, string_desc_t b) |
440 | | { |
441 | | return _sd_equals (a._nbytes, a._data, b._nbytes, b._data); |
442 | | } |
443 | | #endif |
444 | | |
445 | | extern bool _sd_startswith (idx_t s_nbytes, const char *s_data, |
446 | | idx_t prefix_nbytes, const char *prefix_data) |
447 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1) |
448 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3); |
449 | | #if HAVE_RW_STRING_DESC |
450 | | # define sd_startswith(s, prefix) \ |
451 | | ({typeof (s) _s_ = (s); \ |
452 | | typeof (prefix) _prefix_ = (prefix); \ |
453 | | _sd_startswith (_s_._nbytes, _s_._data, _prefix_._nbytes, _prefix_._data); \ |
454 | | }) |
455 | | #else |
456 | | GL_STRING_DESC_INLINE bool |
457 | | sd_startswith (string_desc_t s, string_desc_t prefix) |
458 | | { |
459 | | return _sd_startswith (s._nbytes, s._data, prefix._nbytes, prefix._data); |
460 | | } |
461 | | #endif |
462 | | |
463 | | extern bool _sd_endswith (idx_t s_nbytes, const char *s_data, |
464 | | idx_t suffix_nbytes, const char *suffix_data) |
465 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1) |
466 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3); |
467 | | #if HAVE_RW_STRING_DESC |
468 | | # define sd_endswith(s, suffix) \ |
469 | | ({typeof (s) _s_ = (s); \ |
470 | | typeof (suffix) _suffix_ = (suffix); \ |
471 | | _sd_endswith (_s_._nbytes, _s_._data, _suffix_._nbytes, _suffix_._data); \ |
472 | | }) |
473 | | #else |
474 | | GL_STRING_DESC_INLINE bool |
475 | | sd_endswith (string_desc_t s, string_desc_t suffix) |
476 | | { |
477 | | return _sd_endswith (s._nbytes, s._data, suffix._nbytes, suffix._data); |
478 | | } |
479 | | #endif |
480 | | |
481 | | extern int _sd_cmp (idx_t a_nbytes, const char *a_data, |
482 | | idx_t b_nbytes, const char *b_data) |
483 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1) |
484 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3); |
485 | | #if HAVE_RW_STRING_DESC |
486 | | # define sd_cmp(a, b) \ |
487 | | ({typeof (a) _a_ = (a); \ |
488 | | typeof (b) _b_ = (b); \ |
489 | | _sd_cmp (_a_._nbytes, _a_._data, _b_._nbytes, _b_._data); \ |
490 | | }) |
491 | | #else |
492 | | GL_STRING_DESC_INLINE int |
493 | | sd_cmp (string_desc_t a, string_desc_t b) |
494 | | { |
495 | | return _sd_cmp (a._nbytes, a._data, b._nbytes, b._data); |
496 | | } |
497 | | #endif |
498 | | |
499 | | extern int _sd_c_casecmp (idx_t a_nbytes, const char *a_data, |
500 | | idx_t b_nbytes, const char *b_data) |
501 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1) |
502 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3); |
503 | | #if HAVE_RW_STRING_DESC |
504 | | # define sd_c_casecmp(a, b) \ |
505 | | ({typeof (a) _a_ = (a); \ |
506 | | typeof (b) _b_ = (b); \ |
507 | | _sd_c_casecmp (_a_._nbytes, _a_._data, _b_._nbytes, _b_._data); \ |
508 | | }) |
509 | | #else |
510 | | GL_STRING_DESC_INLINE int |
511 | | sd_c_casecmp (string_desc_t a, string_desc_t b) |
512 | | { |
513 | | return _sd_c_casecmp (a._nbytes, a._data, b._nbytes, b._data); |
514 | | } |
515 | | #endif |
516 | | |
517 | | extern ptrdiff_t _sd_index (idx_t s_nbytes, const char *s_data, char c) |
518 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1); |
519 | | #if HAVE_RW_STRING_DESC |
520 | | # define sd_index(s, c) \ |
521 | | ({typeof (s) _s_ = (s); \ |
522 | | _sd_index (_s_._nbytes, _s_._data, c); \ |
523 | | }) |
524 | | #else |
525 | | GL_STRING_DESC_INLINE ptrdiff_t |
526 | | sd_index (string_desc_t s, char c) |
527 | | { |
528 | | return _sd_index (s._nbytes, s._data, c); |
529 | | } |
530 | | #endif |
531 | | |
532 | | extern ptrdiff_t _sd_last_index (idx_t s_nbytes, const char *s_data, char c) |
533 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1); |
534 | | #if HAVE_RW_STRING_DESC |
535 | | # define sd_last_index(s, c) \ |
536 | | ({typeof (s) _s_ = (s); \ |
537 | | _sd_last_index (_s_._nbytes, _s_._data, c); \ |
538 | | }) |
539 | | #else |
540 | | GL_STRING_DESC_INLINE ptrdiff_t |
541 | | sd_last_index (string_desc_t s, char c) |
542 | | { |
543 | | return _sd_last_index (s._nbytes, s._data, c); |
544 | | } |
545 | | #endif |
546 | | |
547 | | extern ptrdiff_t _sd_contains (idx_t haystack_nbytes, const char *haystack_data, |
548 | | idx_t needle_nbytes, const char *needle_data) |
549 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1) |
550 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3); |
551 | | #if HAVE_RW_STRING_DESC |
552 | | # define sd_contains(haystack, needle) \ |
553 | | ({typeof (haystack) _haystack_ = (haystack); \ |
554 | | typeof (needle) _needle_ = (needle); \ |
555 | | _sd_contains (_haystack_._nbytes, _haystack_._data, \ |
556 | | _needle_._nbytes, _needle_._data); \ |
557 | | }) |
558 | | #else |
559 | | GL_STRING_DESC_INLINE ptrdiff_t |
560 | | sd_contains (string_desc_t haystack, string_desc_t needle) |
561 | | { |
562 | | return _sd_contains (haystack._nbytes, haystack._data, |
563 | | needle._nbytes, needle._data); |
564 | | } |
565 | | #endif |
566 | | |
567 | | extern string_desc_t _sd_new_addr (idx_t n, const char *addr) |
568 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1); |
569 | | extern rw_string_desc_t _rwsd_new_addr (idx_t n, /*!*/const/*!*/ char *addr) |
570 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1); |
571 | | #if HAVE_RW_STRING_DESC |
572 | | # define sd_new_addr(n, addr) \ |
573 | | _Generic (addr, \ |
574 | | const char * : _sd_new_addr (n, addr), \ |
575 | | char * : /* Treat string literals like 'const char *'. */ \ |
576 | | __builtin_choose_expr (__builtin_constant_p (addr), \ |
577 | | _sd_new_addr (n, addr), \ |
578 | | _rwsd_new_addr (n, addr))) |
579 | | #else |
580 | | # define sd_new_addr(n, addr) \ |
581 | | _rwsd_new_addr (n, addr) |
582 | | #endif |
583 | | |
584 | | #if HAVE_RW_STRING_DESC |
585 | | # define sd_substring(s, start, end) \ |
586 | | ({typeof (s) _s_ = (s); \ |
587 | | idx_t _start_ = (start); \ |
588 | | idx_t _end_ = (end); \ |
589 | | if (!(_start_ >= 0 && _start_ <= _end_ && _end_ <= _s_._nbytes)) \ |
590 | | /* Invalid arguments. */ \ |
591 | | abort (); \ |
592 | | typeof (s) _result_; \ |
593 | | _result_._nbytes = _end_ - _start_; \ |
594 | | _result_._data = _s_._data + _start_; \ |
595 | | _result_; \ |
596 | | }) |
597 | | #else |
598 | | GL_STRING_DESC_INLINE string_desc_t |
599 | | sd_substring (string_desc_t s, idx_t start, idx_t end) |
600 | | { |
601 | | if (!(start >= 0 && start <= end && end <= s._nbytes)) |
602 | | /* Invalid arguments. */ |
603 | | abort (); |
604 | | string_desc_t result; |
605 | | result._nbytes = end - start; |
606 | | result._data = s._data + start; |
607 | | return result; |
608 | | } |
609 | | #endif |
610 | | |
611 | | extern int _sd_write (int fd, idx_t s_nbytes, const char *s_data) |
612 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2); |
613 | | #if HAVE_RW_STRING_DESC |
614 | | # define sd_write(fd, s) \ |
615 | | ({int _fd_ = (fd); \ |
616 | | typeof (s) _s_ = (s); \ |
617 | | _sd_write (_fd_, _s_._nbytes, _s_._data); \ |
618 | | }) |
619 | | #else |
620 | | GL_STRING_DESC_INLINE int |
621 | | sd_write (int fd, string_desc_t s) |
622 | | { |
623 | | return _sd_write (fd, s._nbytes, s._data); |
624 | | } |
625 | | #endif |
626 | | |
627 | | extern int _sd_fwrite (FILE *fp, idx_t s_nbytes, const char *s_data) |
628 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2); |
629 | | #if HAVE_RW_STRING_DESC |
630 | | # define sd_fwrite(fp, s) \ |
631 | | ({FILE *_fp_ = (fp); \ |
632 | | typeof (s) _s_ = (s); \ |
633 | | _sd_fwrite (_fp_, _s_._nbytes, _s_._data); \ |
634 | | }) |
635 | | #else |
636 | | GL_STRING_DESC_INLINE int |
637 | | sd_fwrite (FILE *fp, string_desc_t s) |
638 | | { |
639 | | return _sd_fwrite (fp, s._nbytes, s._data); |
640 | | } |
641 | | #endif |
642 | | |
643 | | extern int _sd_copy (rw_string_desc_t *resultp, |
644 | | idx_t s_nbytes, const char *s_data) |
645 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2); |
646 | | #if HAVE_RW_STRING_DESC |
647 | | # define sd_copy(resultp, s) \ |
648 | | ({rw_string_desc_t *_resultp_ = (resultp); \ |
649 | | typeof (s) _s_ = (s); \ |
650 | | _sd_copy (_resultp_, _s_._nbytes, _s_._data); \ |
651 | | }) |
652 | | #else |
653 | | _GL_ATTRIBUTE_NODISCARD GL_STRING_DESC_INLINE int |
654 | | sd_copy (rw_string_desc_t *resultp, string_desc_t s) |
655 | | { |
656 | | return _sd_copy (resultp, s._nbytes, s._data); |
657 | | } |
658 | | #endif |
659 | | |
660 | | extern char *_sd_c (idx_t s_nbytes, const char *s_data) |
661 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1); |
662 | | #if HAVE_RW_STRING_DESC |
663 | | # define sd_c(s) \ |
664 | | ({typeof (s) _s_ = (s); \ |
665 | | _sd_c (_s_._nbytes, _s_._data); \ |
666 | | }) |
667 | | #else |
668 | | GL_STRING_DESC_INLINE char * |
669 | | sd_c (string_desc_t s) |
670 | | { |
671 | | return _sd_c (s._nbytes, s._data); |
672 | | } |
673 | | #endif |
674 | | |
675 | | extern void _sd_overwrite (rw_string_desc_t s, idx_t start, |
676 | | idx_t t_nbytes, const char *t_data) |
677 | | _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3); |
678 | | #if HAVE_RW_STRING_DESC |
679 | | # define sd_overwrite(s, start, t) \ |
680 | | ({rw_string_desc_t _s_ = (s); \ |
681 | | idx_t _start_ = (start); \ |
682 | | typeof (t) _t_ = (t); \ |
683 | | _sd_overwrite (_s_, _start_, _t_._nbytes, _t_._data); \ |
684 | | }) |
685 | | #else |
686 | | GL_STRING_DESC_INLINE void |
687 | | sd_overwrite (rw_string_desc_t s, idx_t start, string_desc_t t) |
688 | | { |
689 | | _sd_overwrite (s, start, t._nbytes, t._data); |
690 | | } |
691 | | #endif |
692 | | |
693 | | |
694 | | #ifdef __cplusplus |
695 | | } |
696 | | #endif |
697 | | |
698 | | _GL_INLINE_HEADER_END |
699 | | |
700 | | |
701 | | #endif /* _STRING_DESC_H */ |