Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */