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