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