]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
string-desc: Distinguish writable strings from read-only strings.
authorBruno Haible <bruno@clisp.org>
Sat, 10 May 2025 01:16:52 +0000 (03:16 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 10 May 2025 01:20:41 +0000 (03:20 +0200)
* lib/string-desc.h (HAVE_STATEMENT_EXPRESSIONS): New macro.
(rw_string_desc_t): New type.
(string_desc_t) [HAVE_STATEMENT_EXPRESSIONS]: Change field _data from
'char *' to 'const char *'.
(sd_readonly, sd_readwrite): New inline functions.
(sd_length): Define through a macro with _Generic.
(sd_char_at): Define through a macro and an inline function.
(sd_data, sd_is_empty): Define through a macro with _Generic.
(sd_equals, sd_startswith, sd_endswith, sd_cmp, sd_c_casecmp, sd_index,
sd_last_index, sd_contains): Define through a macro.
(sd_new_addr): Define through a macro with _Generic.
(sd_substring, sd_write, sd_fwrite): Define through a macro.
(sd_new, sd_new_filled): Change parameter type.
(sd_copy): Define through a macro.
(sd_concat): Change parameter type.
(sd_c): Define through a macro.
(sd_set_char_at, sd_fill): Change parameter type.
(sd_overwrite): Define through a macro.
(sd_free): Change parameter type.
* lib/string-desc.c (_sd_equals): Renamed from sd_equals. Take scalar
parameters.
(_sd_startswith): Renamed from sd_startswith. Take scalar parameters.
(_sd_endswith): Renamed from sd_endswith. Take scalar parameters.
(_sd_cmp): Renamed from sd_cmp. Take scalar parameters.
(_sd_c_casecmp): Renamed from sd_c_casecmp. Take scalar parameters.
(_sd_index): Renamed from sd_index. Take scalar parameters.
(_sd_last_index): Renamed from sd_last_index. Take scalar parameters.
(_sd_new_addr, _rwsd_new_addr): Renamed from sd_new_addr.
(sd_substring): Remove function.
(_sd_write): Renamed from sd_write. Take scalar parameters.
(_sd_fwrite): Renamed from sd_fwrite. Take scalar parameters.
(sd_new, sd_new_filled): Change parameter type.
(_sd_copy): Renamed from sd_copy. Change parameter type. Take scalar
parameters.
(sd_concat): Change parameter type.
(_sd_c): Renamed from sd_c. Take scalar parameters.
(sd_set_char_at, sd_fill): Change parameter type.
(_sd_overwrite): Renamed from sd_overwrite. Change parameter type. Take
scalar parameters.
(sd_free): Change parameter type.
* lib/string-desc-contains.c (_sd_contains): Renamed from sd_contains.
Take scalar parameters.
* lib/xstring-desc.h (xsd_new, xsd_new_filled, xsd_copy, xsd_concat):
Change return type to rw_string_desc_t.
(xsd_c): Define through a macro.
* lib/xstring-desc.c (xsd_concat): Change return type to
rw_string_desc_t.
* doc/string-desc.texi (Handling strings with NUL characters): Mention
rw_string_desc_t and the sd_readonly() function.
* lib/string-buffer.h (sb_dupfree, sb_xdupfree): Change return type to
rw_string_desc_t.
* lib/string-buffer.c (sb_contents): Add a cast to 'const char *'.
(sb_dupfree): Change return type to rw_string_desc_t.
* lib/xstring-buffer.c (sb_xdupfree): Change return type to
rw_string_desc_t.
* lib/string-buffer-reversed.h (sbr_dupfree, sbr_xdupfree): Change
return type to rw_string_desc_t.
* lib/string-buffer-reversed.c (sbr_contents): Add a cast to
'const char *'.
(sbr_dupfree): Change return type to rw_string_desc_t.
* lib/xstring-buffer-reversed.c (sbr_xdupfree): Change return type to
rw_string_desc_t.
* tests/test-string-desc.c (main): Use type rw_string_desc_t as
appropriate.
* tests/test-xstring-desc.c (main): Likewise.
* tests/test-sf-istream.c (main): Remove cast in sd_new_addr argument.
* tests/test-sfl-istream.c (main): Likewise.
* NEWS: Mention the change.

18 files changed:
ChangeLog
NEWS
doc/string-desc.texi
lib/string-buffer-reversed.c
lib/string-buffer-reversed.h
lib/string-buffer.c
lib/string-buffer.h
lib/string-desc-contains.c
lib/string-desc.c
lib/string-desc.h
lib/xstring-buffer-reversed.c
lib/xstring-buffer.c
lib/xstring-desc.c
lib/xstring-desc.h
tests/test-sf-istream.c
tests/test-sfl-istream.c
tests/test-string-desc.c
tests/test-xstring-desc.c

index 842a40a47f9d268fb037464f1457ab648a22e705..39e2ae752de41191277637c305ddd26ac450f720 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,75 @@
+2025-05-09  Bruno Haible  <bruno@clisp.org>
+
+       string-desc: Distinguish writable strings from read-only strings.
+       * lib/string-desc.h (HAVE_STATEMENT_EXPRESSIONS): New macro.
+       (rw_string_desc_t): New type.
+       (string_desc_t) [HAVE_STATEMENT_EXPRESSIONS]: Change field _data from
+       'char *' to 'const char *'.
+       (sd_readonly, sd_readwrite): New inline functions.
+       (sd_length): Define through a macro with _Generic.
+       (sd_char_at): Define through a macro and an inline function.
+       (sd_data, sd_is_empty): Define through a macro with _Generic.
+       (sd_equals, sd_startswith, sd_endswith, sd_cmp, sd_c_casecmp, sd_index,
+       sd_last_index, sd_contains): Define through a macro.
+       (sd_new_addr): Define through a macro with _Generic.
+       (sd_substring, sd_write, sd_fwrite): Define through a macro.
+       (sd_new, sd_new_filled): Change parameter type.
+       (sd_copy): Define through a macro.
+       (sd_concat): Change parameter type.
+       (sd_c): Define through a macro.
+       (sd_set_char_at, sd_fill): Change parameter type.
+       (sd_overwrite): Define through a macro.
+       (sd_free): Change parameter type.
+       * lib/string-desc.c (_sd_equals): Renamed from sd_equals. Take scalar
+       parameters.
+       (_sd_startswith): Renamed from sd_startswith. Take scalar parameters.
+       (_sd_endswith): Renamed from sd_endswith. Take scalar parameters.
+       (_sd_cmp): Renamed from sd_cmp. Take scalar parameters.
+       (_sd_c_casecmp): Renamed from sd_c_casecmp. Take scalar parameters.
+       (_sd_index): Renamed from sd_index. Take scalar parameters.
+       (_sd_last_index): Renamed from sd_last_index. Take scalar parameters.
+       (_sd_new_addr, _rwsd_new_addr): Renamed from sd_new_addr.
+       (sd_substring): Remove function.
+       (_sd_write): Renamed from sd_write. Take scalar parameters.
+       (_sd_fwrite): Renamed from sd_fwrite. Take scalar parameters.
+       (sd_new, sd_new_filled): Change parameter type.
+       (_sd_copy): Renamed from sd_copy. Change parameter type. Take scalar
+       parameters.
+       (sd_concat): Change parameter type.
+       (_sd_c): Renamed from sd_c. Take scalar parameters.
+       (sd_set_char_at, sd_fill): Change parameter type.
+       (_sd_overwrite): Renamed from sd_overwrite. Change parameter type. Take
+       scalar parameters.
+       (sd_free): Change parameter type.
+       * lib/string-desc-contains.c (_sd_contains): Renamed from sd_contains.
+       Take scalar parameters.
+       * lib/xstring-desc.h (xsd_new, xsd_new_filled, xsd_copy, xsd_concat):
+       Change return type to rw_string_desc_t.
+       (xsd_c): Define through a macro.
+       * lib/xstring-desc.c (xsd_concat): Change return type to
+       rw_string_desc_t.
+       * doc/string-desc.texi (Handling strings with NUL characters): Mention
+       rw_string_desc_t and the sd_readonly() function.
+       * lib/string-buffer.h (sb_dupfree, sb_xdupfree): Change return type to
+       rw_string_desc_t.
+       * lib/string-buffer.c (sb_contents): Add a cast to 'const char *'.
+       (sb_dupfree): Change return type to rw_string_desc_t.
+       * lib/xstring-buffer.c (sb_xdupfree): Change return type to
+       rw_string_desc_t.
+       * lib/string-buffer-reversed.h (sbr_dupfree, sbr_xdupfree): Change
+       return type to rw_string_desc_t.
+       * lib/string-buffer-reversed.c (sbr_contents): Add a cast to
+       'const char *'.
+       (sbr_dupfree): Change return type to rw_string_desc_t.
+       * lib/xstring-buffer-reversed.c (sbr_xdupfree): Change return type to
+       rw_string_desc_t.
+       * tests/test-string-desc.c (main): Use type rw_string_desc_t as
+       appropriate.
+       * tests/test-xstring-desc.c (main): Likewise.
+       * tests/test-sf-istream.c (main): Remove cast in sd_new_addr argument.
+       * tests/test-sfl-istream.c (main): Likewise.
+       * NEWS: Mention the change.
+
 2025-05-09  Paul Eggert  <eggert@cs.ucla.edu>
 
        qcopy-acl: port better to NFSv4 on GNU/Linux
diff --git a/NEWS b/NEWS
index 639760a5b9943283f4cdcc5a63276bc93f9740c9..5499535f2a368dc51533f0ae01b208fa0e1f7462 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -74,6 +74,10 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2025-05-09  string-desc     These modules now distinguish between read-only
+            xstring-desc    string descriptors (type string_desc_t) and
+                            writable string descriptors (type rw_string_desc_t).
+
 2025-05-09  copy-acl        The mode_t arguments of qcopy_acl and xcopy_acl
             qcopy-acl       should be the full st_mode of the source,
                             not merely st_mode's permission bits.
index ab5c5fced3f5cc2096175d7122d5ea9d9654c530..997c384302610a040736287fe060d8458380a6e1 100644 (file)
@@ -53,13 +53,27 @@ memory, is not included in this byte count.  This type implements the
 same concept as @code{std::string_view} in C++, or the @code{String}
 type in Java.
 
-A @code{string_desc_t} can be passed to a function as an argument, or
+@code{string_desc_t} is a string descriptor to a string that cannot
+be written to.  There is also a type @code{rw_string_desc_t}, that is
+a descriptor for a writable string.
+@code{rw_string_desc_t} compares to @code{string_desc_t}, like the
+pointer type @samp{char *} compares to the pointer type
+@samp{const char *}.
+
+A @code{string_desc_t} or @code{rw_string_desc_t}
+can be passed to a function as an argument, or
 can be the return value of a function.  This is type-safe: If, by
 mistake, a programmer passes a @code{string_desc_t} to a function that
 expects a @code{char *} argument, or vice versa, or assigns a
 @code{string_desc_t} value to a variable of type @code{char *}, or
 vice versa, the compiler will report an error.
 
+Unfortunately, @code{string_desc_t} and @code{rw_string_desc_t}
+being different types, there is no implicit conversion from
+@code{rw_string_desc_t} to @code{string_desc_t}.  In places
+where such a conversion is desired, the (inline) function
+@code{sd_readonly} needs to be called.
+
 Functions related to string descriptors are provided:
 @itemize
 @item
index 4f093f66dcffdd4669dc180b2a26de733c298410..8b55650bdfbcac6b1ef556f28808e6f6264def73 100644 (file)
@@ -148,7 +148,7 @@ string_desc_t
 sbr_contents (struct string_buffer_reversed *buffer)
 {
   return sd_new_addr (buffer->length - 1,
-                      buffer->data + buffer->allocated - buffer->length);
+                      (const char *) buffer->data + buffer->allocated - buffer->length);
 }
 
 const char *
@@ -157,7 +157,7 @@ sbr_contents_c (struct string_buffer_reversed *buffer)
   return buffer->data + buffer->allocated - buffer->length;
 }
 
-string_desc_t
+rw_string_desc_t
 sbr_dupfree (struct string_buffer_reversed *buffer)
 {
   if (buffer->oom || buffer->error)
@@ -185,7 +185,7 @@ sbr_dupfree (struct string_buffer_reversed *buffer)
 
  fail:
   sbr_free (buffer);
-  return sd_new_addr (0, NULL);
+  return sd_new_addr (0, (char *) NULL);
 }
 
 char *
index 021508c2e9e389612acd730605b3998d4fc0753a..957af427b5a807249b08f32ff7b6f6efb4f48936 100644 (file)
@@ -131,7 +131,7 @@ extern const char * sbr_contents_c (struct string_buffer_reversed *buffer);
 /* Returns the contents of BUFFER and frees all other memory held by BUFFER.
    Returns NULL upon failure or if there was an error earlier.
    It is the responsibility of the caller to sd_free() the result.  */
-extern string_desc_t sbr_dupfree (struct string_buffer_reversed *buffer)
+extern rw_string_desc_t sbr_dupfree (struct string_buffer_reversed *buffer)
   _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
 
 /* Returns the contents of BUFFER (with an added trailing NUL, that is,
@@ -196,7 +196,7 @@ extern int sbr_xprependf (struct string_buffer_reversed *buffer,
 /* Returns the contents of BUFFER and frees all other memory held by BUFFER.
    Returns (0, NULL) if there was an error earlier.
    It is the responsibility of the caller to sd_free() the result.  */
-extern string_desc_t sbr_xdupfree (struct string_buffer_reversed *buffer)
+extern rw_string_desc_t sbr_xdupfree (struct string_buffer_reversed *buffer)
   _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
 
 /* Returns the contents of BUFFER (with an added trailing NUL, that is,
index ac1606f23d9d91a979c7f8c17cbdd6d816640854..50a326dd23e73be4160196c82368253444a82bf1 100644 (file)
@@ -137,7 +137,7 @@ sb_free (struct string_buffer *buffer)
 string_desc_t
 sb_contents (struct string_buffer *buffer)
 {
-  return sd_new_addr (buffer->length, buffer->data);
+  return sd_new_addr (buffer->length, (const char *) buffer->data);
 }
 
 const char *
@@ -150,7 +150,7 @@ sb_contents_c (struct string_buffer *buffer)
   return buffer->data;
 }
 
-string_desc_t
+rw_string_desc_t
 sb_dupfree (struct string_buffer *buffer)
 {
   if (buffer->oom || buffer->error)
@@ -180,7 +180,7 @@ sb_dupfree (struct string_buffer *buffer)
 
  fail:
   sb_free (buffer);
-  return sd_new_addr (0, NULL);
+  return sd_new_addr (0, (char *) NULL);
 }
 
 char *
index eefdd195d7f961cf0611394357c02fee1c707268..99f5d40bb09cbf530472241aafbe934b1fcc2589 100644 (file)
@@ -129,7 +129,7 @@ extern const char * sb_contents_c (struct string_buffer *buffer);
 /* Returns the contents of BUFFER and frees all other memory held by BUFFER.
    Returns NULL upon failure or if there was an error earlier.
    It is the responsibility of the caller to sd_free() the result.  */
-extern string_desc_t sb_dupfree (struct string_buffer *buffer)
+extern rw_string_desc_t sb_dupfree (struct string_buffer *buffer)
   _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
 
 /* Returns the contents of BUFFER (with an added trailing NUL, that is,
@@ -200,7 +200,7 @@ extern const char * sb_xcontents_c (struct string_buffer *buffer)
 /* Returns the contents of BUFFER and frees all other memory held by BUFFER.
    Returns (0, NULL) if there was an error earlier.
    It is the responsibility of the caller to sd_free() the result.  */
-extern string_desc_t sb_xdupfree (struct string_buffer *buffer)
+extern rw_string_desc_t sb_xdupfree (struct string_buffer *buffer)
   _GL_ATTRIBUTE_RELEASE_CAPABILITY (buffer->data);
 
 /* Returns the contents of BUFFER (with an added trailing NUL, that is,
index 18657c70ff641e4648ae4bb230b8d530302a9201..b7943bcddde784c35435b5b104309f18be183814 100644 (file)
    which — depending on platforms — costs up to 2 KB of binary code.  */
 
 ptrdiff_t
-sd_contains (string_desc_t haystack, string_desc_t needle)
+_sd_contains (idx_t haystack_nbytes, const char *haystack_data,
+              idx_t needle_nbytes, const char *needle_data)
 {
-  if (needle._nbytes == 0)
+  if (needle_nbytes == 0)
     return 0;
-  if (haystack._nbytes == 0)
+  if (haystack_nbytes == 0)
     return -1;
   void *found =
-    memmem (haystack._data, haystack._nbytes, needle._data, needle._nbytes);
+    memmem (haystack_data, haystack_nbytes, needle_data, needle_nbytes);
   if (found != NULL)
-    return (char *) found - haystack._data;
+    return (char *) found - haystack_data;
   else
     return -1;
 }
index dc13f2f15004b2cf86a6da1cbc478f7506d0c1d3..7cabe4ed84c0fe398c549ab44b536f206304eddd 100644 (file)
 
 /* Return true if A and B are equal.  */
 bool
-sd_equals (string_desc_t a, string_desc_t b)
+_sd_equals (idx_t a_nbytes, const char *a_data,
+            idx_t b_nbytes, const char *b_data)
 {
-  return (a._nbytes == b._nbytes
-          && (a._nbytes == 0 || memcmp (a._data, b._data, a._nbytes) == 0));
+  return (a_nbytes == b_nbytes
+          && (a_nbytes == 0 || memcmp (a_data, b_data, a_nbytes) == 0));
 }
 
 bool
-sd_startswith (string_desc_t s, string_desc_t prefix)
+_sd_startswith (idx_t s_nbytes, const char *s_data,
+                idx_t prefix_nbytes, const char *prefix_data)
 {
-  return (s._nbytes >= prefix._nbytes
-          && (prefix._nbytes == 0
-              || memcmp (s._data, prefix._data, prefix._nbytes) == 0));
+  return (s_nbytes >= prefix_nbytes
+          && (prefix_nbytes == 0
+              || memcmp (s_data, prefix_data, prefix_nbytes) == 0));
 }
 
 bool
-sd_endswith (string_desc_t s, string_desc_t suffix)
+_sd_endswith (idx_t s_nbytes, const char *s_data,
+                idx_t suffix_nbytes, const char *suffix_data)
 {
-  return (s._nbytes >= suffix._nbytes
-          && (suffix._nbytes == 0
-              || memcmp (s._data + (s._nbytes - suffix._nbytes), suffix._data,
-                         suffix._nbytes) == 0));
+  return (s_nbytes >= suffix_nbytes
+          && (suffix_nbytes == 0
+              || memcmp (s_data + (s_nbytes - suffix_nbytes), suffix_data,
+                         suffix_nbytes) == 0));
 }
 
 int
-sd_cmp (string_desc_t a, string_desc_t b)
+_sd_cmp (idx_t a_nbytes, const char *a_data,
+         idx_t b_nbytes, const char *b_data)
 {
-  if (a._nbytes > b._nbytes)
+  if (a_nbytes > b_nbytes)
     {
-      if (b._nbytes == 0)
+      if (b_nbytes == 0)
         return 1;
-      return (memcmp (a._data, b._data, b._nbytes) < 0 ? -1 : 1);
+      return (memcmp (a_data, b_data, b_nbytes) < 0 ? -1 : 1);
     }
-  else if (a._nbytes < b._nbytes)
+  else if (a_nbytes < b_nbytes)
     {
-      if (a._nbytes == 0)
+      if (a_nbytes == 0)
         return -1;
-      return (memcmp (a._data, b._data, a._nbytes) > 0 ? 1 : -1);
+      return (memcmp (a_data, b_data, a_nbytes) > 0 ? 1 : -1);
     }
-  else /* a._nbytes == b._nbytes */
+  else /* a_nbytes == b_nbytes */
     {
-      if (a._nbytes == 0)
+      if (a_nbytes == 0)
         return 0;
-      return memcmp (a._data, b._data, a._nbytes);
+      return memcmp (a_data, b_data, a_nbytes);
     }
 }
 
 int
-sd_c_casecmp (string_desc_t a, string_desc_t b)
+_sd_c_casecmp (idx_t a_nbytes, const char *a_data,
+               idx_t b_nbytes, const char *b_data)
 {
   /* Don't use memcasecmp here, since it uses the current locale, not the
      "C" locale.  */
-  idx_t an = sd_length (a);
-  idx_t bn = sd_length (b);
-  const char *ap = sd_data (a);
-  const char *bp = sd_data (b);
-  idx_t n = (an < bn ? an : bn);
+  idx_t n = (a_nbytes < b_nbytes ? a_nbytes : b_nbytes);
   idx_t i;
   for (i = 0; i < n; i++)
     {
-      int ac = c_tolower ((unsigned char) ap[i]);
-      int bc = c_tolower ((unsigned char) bp[i]);
+      int ac = c_tolower ((unsigned char) a_data[i]);
+      int bc = c_tolower ((unsigned char) b_data[i]);
       if (ac != bc)
         return (UCHAR_MAX <= INT_MAX ? ac - bc : _GL_CMP (ac, bc));
     }
-  /* Here i = n = min (an, bn).  */
-  return _GL_CMP (an, bn);
+  /* Here i = n = min (a_nbytes, b_nbytes).  */
+  return _GL_CMP (a_nbytes, b_nbytes);
 }
 
 ptrdiff_t
-sd_index (string_desc_t s, char c)
+_sd_index (idx_t s_nbytes, const char *s_data, char c)
 {
-  if (s._nbytes > 0)
+  if (s_nbytes > 0)
     {
-      void *found = memchr (s._data, (unsigned char) c, s._nbytes);
+      void *found = memchr (s_data, (unsigned char) c, s_nbytes);
       if (found != NULL)
-        return (char *) found - s._data;
+        return (char *) found - s_data;
     }
   return -1;
 }
 
 ptrdiff_t
-sd_last_index (string_desc_t s, char c)
+_sd_last_index (idx_t s_nbytes, const char *s_data, char c)
 {
-  if (s._nbytes > 0)
+  if (s_nbytes > 0)
     {
-      void *found = memrchr (s._data, (unsigned char) c, s._nbytes);
+      void *found = memrchr (s_data, (unsigned char) c, s_nbytes);
       if (found != NULL)
-        return (char *) found - s._data;
+        return (char *) found - s_data;
     }
   return -1;
 }
@@ -140,11 +141,15 @@ sd_new_empty (void)
   result._data = NULL;
 
   return result;
-
 }
 
+#if __GNUC__ >= 5
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
+#endif
+
 string_desc_t
-sd_new_addr (idx_t n, char *addr)
+_sd_new_addr (idx_t n, const char *addr)
 {
   string_desc_t result;
 
@@ -152,52 +157,54 @@ sd_new_addr (idx_t n, char *addr)
   if (n == 0)
     result._data = NULL;
   else
-    result._data = addr;
+    result._data = (char *) addr;
 
   return result;
 }
-
-string_desc_t
-sd_from_c (const char *s)
+rw_string_desc_t
+_rwsd_new_addr (idx_t n, /*!*/const/*!*/ char *addr)
 {
-  string_desc_t result;
+  rw_string_desc_t result;
 
-  result._nbytes = strlen (s);
-  result._data = (char *) s;
+  result._nbytes = n;
+  if (n == 0)
+    result._data = NULL;
+  else
+    result._data = (char *) addr;
 
   return result;
 }
 
 string_desc_t
-sd_substring (string_desc_t s, idx_t start, idx_t end)
+sd_from_c (const char *s)
 {
   string_desc_t result;
 
-  if (!(start >= 0 && start <= end && end <= s._nbytes))
-    /* Invalid arguments.  */
-    abort ();
-
-  result._nbytes = end - start;
-  result._data = s._data + start;
+  result._nbytes = strlen (s);
+  result._data = (char *) s;
 
   return result;
 }
 
+#if __GNUC__ >= 5
+# pragma GCC diagnostic pop
+#endif
+
 int
-sd_write (int fd, string_desc_t s)
+_sd_write (int fd, idx_t s_nbytes, const char *s_data)
 {
-  if (s._nbytes > 0)
-    if (full_write (fd, s._data, s._nbytes) != s._nbytes)
+  if (s_nbytes > 0)
+    if (full_write (fd, s_data, s_nbytes) != s_nbytes)
       /* errno is set here.  */
       return -1;
   return 0;
 }
 
 int
-sd_fwrite (FILE *fp, string_desc_t s)
+_sd_fwrite (FILE *fp, idx_t s_nbytes, const char *s_data)
 {
-  if (s._nbytes > 0)
-    if (fwrite (s._data, 1, s._nbytes, fp) != s._nbytes)
+  if (s_nbytes > 0)
+    if (fwrite (s_data, 1, s_nbytes, fp) != s_nbytes)
       return -1;
   return 0;
 }
@@ -206,9 +213,9 @@ sd_fwrite (FILE *fp, string_desc_t s)
 /* ==== Memory-allocating operations on string descriptors ==== */
 
 int
-sd_new (string_desc_t *resultp, idx_t n)
+sd_new (rw_string_desc_t *resultp, idx_t n)
 {
-  string_desc_t result;
+  rw_string_desc_t result;
 
   if (!(n >= 0))
     /* Invalid argument.  */
@@ -230,9 +237,9 @@ sd_new (string_desc_t *resultp, idx_t n)
 }
 
 int
-sd_new_filled (string_desc_t *resultp, idx_t n, char c)
+sd_new_filled (rw_string_desc_t *resultp, idx_t n, char c)
 {
-  string_desc_t result;
+  rw_string_desc_t result;
 
   result._nbytes = n;
   if (n == 0)
@@ -251,10 +258,10 @@ sd_new_filled (string_desc_t *resultp, idx_t n, char c)
 }
 
 int
-sd_copy (string_desc_t *resultp, string_desc_t s)
+_sd_copy (rw_string_desc_t *resultp, idx_t s_nbytes, const char *s_data)
 {
-  string_desc_t result;
-  idx_t n = s._nbytes;
+  rw_string_desc_t result;
+  idx_t n = s_nbytes;
 
   result._nbytes = n;
   if (n == 0)
@@ -265,7 +272,7 @@ sd_copy (string_desc_t *resultp, string_desc_t s)
       if (result._data == NULL)
         /* errno is set here.  */
         return -1;
-      memcpy (result._data, s._data, n);
+      memcpy (result._data, s_data, n);
     }
 
   *resultp = result;
@@ -273,52 +280,58 @@ sd_copy (string_desc_t *resultp, string_desc_t s)
 }
 
 int
-sd_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...)
+sd_concat (rw_string_desc_t *resultp, idx_t n, /* [rw_]string_desc_t string1, */ ...)
 {
   if (n <= 0)
     /* Invalid argument.  */
     abort ();
 
   idx_t total = 0;
-  total += string1._nbytes;
-  if (n > 1)
-    {
-      va_list other_strings;
-      idx_t i;
-
-      va_start (other_strings, string1);
-      for (i = n - 1; i > 0; i--)
-        {
-          string_desc_t arg = va_arg (other_strings, string_desc_t);
-          total += arg._nbytes;
-        }
-      va_end (other_strings);
-    }
+  {
+    va_list strings;
+    va_start (strings, n);
+    string_desc_t string1 = va_arg (strings, string_desc_t);
+    total += string1._nbytes;
+    if (n > 1)
+      {
+        idx_t i;
+
+        for (i = n - 1; i > 0; i--)
+          {
+            string_desc_t arg = va_arg (strings, string_desc_t);
+            total += arg._nbytes;
+          }
+      }
+    va_end (strings);
+  }
 
   char *combined = (char *) imalloc (total);
   if (combined == NULL)
     /* errno is set here.  */
     return -1;
   idx_t pos = 0;
-  memcpy (combined, string1._data, string1._nbytes);
-  pos += string1._nbytes;
-  if (n > 1)
-    {
-      va_list other_strings;
-      idx_t i;
-
-      va_start (other_strings, string1);
-      for (i = n - 1; i > 0; i--)
-        {
-          string_desc_t arg = va_arg (other_strings, string_desc_t);
-          if (arg._nbytes > 0)
-            memcpy (combined + pos, arg._data, arg._nbytes);
-          pos += arg._nbytes;
-        }
-      va_end (other_strings);
-    }
-
-  string_desc_t result;
+  {
+    va_list strings;
+    va_start (strings, n);
+    string_desc_t string1 = va_arg (strings, string_desc_t);
+    memcpy (combined, string1._data, string1._nbytes);
+    pos += string1._nbytes;
+    if (n > 1)
+      {
+        idx_t i;
+
+        for (i = n - 1; i > 0; i--)
+          {
+            string_desc_t arg = va_arg (strings, string_desc_t);
+            if (arg._nbytes > 0)
+              memcpy (combined + pos, arg._data, arg._nbytes);
+            pos += arg._nbytes;
+          }
+      }
+    va_end (strings);
+  }
+
+  rw_string_desc_t result;
   result._nbytes = total;
   result._data = combined;
 
@@ -327,15 +340,15 @@ sd_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...)
 }
 
 char *
-sd_c (string_desc_t s)
+_sd_c (idx_t s_nbytes, const char *s_data)
 {
-  idx_t n = s._nbytes;
+  idx_t n = s_nbytes;
   char *result = (char *) imalloc (n + 1);
   if (result == NULL)
     /* errno is set here.  */
     return NULL;
   if (n > 0)
-    memcpy (result, s._data, n);
+    memcpy (result, s_data, n);
   result[n] = '\0';
 
   return result;
@@ -345,7 +358,7 @@ sd_c (string_desc_t s)
 /* ==== Operations with side effects on string descriptors ==== */
 
 void
-sd_set_char_at (string_desc_t s, idx_t i, char c)
+sd_set_char_at (rw_string_desc_t s, idx_t i, char c)
 {
   if (!(i >= 0 && i < s._nbytes))
     /* Invalid argument.  */
@@ -354,7 +367,7 @@ sd_set_char_at (string_desc_t s, idx_t i, char c)
 }
 
 void
-sd_fill (string_desc_t s, idx_t start, idx_t end, char c)
+sd_fill (rw_string_desc_t s, idx_t start, idx_t end, char c)
 {
   if (!(start >= 0 && start <= end))
     /* Invalid arguments.  */
@@ -365,18 +378,18 @@ sd_fill (string_desc_t s, idx_t start, idx_t end, char c)
 }
 
 void
-sd_overwrite (string_desc_t s, idx_t start, string_desc_t t)
+_sd_overwrite (rw_string_desc_t s, idx_t start, idx_t t_nbytes, const char *t_data)
 {
-  if (!(start >= 0 && start + t._nbytes <= s._nbytes))
+  if (!(start >= 0 && start + t_nbytes <= s._nbytes))
     /* Invalid arguments.  */
     abort ();
 
-  if (t._nbytes > 0)
-    memcpy (s._data + start, t._data, t._nbytes);
+  if (t_nbytes > 0)
+    memcpy (s._data + start, t_data, t_nbytes);
 }
 
 void
-sd_free (string_desc_t s)
+sd_free (rw_string_desc_t s)
 {
   free (s._data);
 }
index e2de97af51cc3776d1e920c32779558ca98b42c7..114f8f3b574b6333332fad03390ebe459b31d5a0 100644 (file)
 /* Get idx_t.  */
 #include "idx.h"
 
+/* Whether the compiler supports statement-expressions.  */
+#if defined __GNUC__ || defined __clang__ \
+    || (defined __SUNPRO_C && __SUNPRO_C >= 0x5150)
+# define HAVE_STATEMENT_EXPRESSIONS 1
+#endif
 
 _GL_INLINE_HEADER_BEGIN
 #ifndef GL_STRING_DESC_INLINE
@@ -49,14 +54,31 @@ extern "C" {
 
 
 /* Type describing a string that may contain NUL bytes.
-   It's merely a descriptor of an array of bytes.  */
+   It's merely a descriptor of an array of bytes.
+   It comes in two flavours:
+     - string_desc_t is read-only,
+     - rw_string_desc_t is read-write.
+   When we write
+     [rw_]string_desc_t
+   it means either string_desc_t or rw_string_desc_t.  */
+typedef struct rw_string_desc_t rw_string_desc_t;
+struct rw_string_desc_t
+{
+  /* The fields of this struct should be considered private.  */
+  idx_t _nbytes;
+  char *_data;
+};
+#if HAVE_STATEMENT_EXPRESSIONS
 typedef struct string_desc_t string_desc_t;
 struct string_desc_t
 {
   /* The fields of this struct should be considered private.  */
   idx_t _nbytes;
-  char *_data;
+  const char *_data;
 };
+#else
+typedef rw_string_desc_t string_desc_t;
+#endif
 
 /* String descriptors can be passed and returned by value.
 
@@ -65,67 +87,103 @@ struct string_desc_t
    attempt to assign a string descriptor to a C string or vice versa.  */
 
 
+/* ==== Conversions between string descriptors ==== */
+
+/* Return a read-only view of S.  */
+#if 0 /* Defined inline below.  */
+extern string_desc_t sd_readonly (rw_string_desc_t s);
+#endif
+
+/* Return a read-only view of S.
+   Attention: This function is as dangerous as a cast from 'const char *'
+   to 'char *'!  */
+#if 0 /* Defined inline below.  */
+extern rw_string_desc_t sd_readwrite (string_desc_t s);
+#endif
+
+
 /* ==== Side-effect-free operations on string descriptors ==== */
 
 /* Return the length of the string S.  */
 #if 0 /* Defined inline below.  */
-extern idx_t sd_length (string_desc_t s);
+extern idx_t sd_length ([rw_]string_desc_t s);
 #endif
 
 /* Return the byte at index I of string S.
    I must be < length(S).  */
 #if 0 /* Defined inline below.  */
-extern char sd_char_at (string_desc_t s, idx_t i);
+extern char sd_char_at ([rw_]string_desc_t s, idx_t i);
 #endif
 
 /* Return a read-only view of the bytes of S.  */
 #if 0 /* Defined inline below.  */
 extern const char * sd_data (string_desc_t s);
+extern       char * sd_data (rw_string_desc_t s);
 #endif
 
 /* Return true if S is the empty string.  */
 #if 0 /* Defined inline below.  */
-extern bool sd_is_empty (string_desc_t s);
+extern bool sd_is_empty ([rw_]string_desc_t s);
 #endif
 
 /* Return true if A and B are equal.  */
-extern bool sd_equals (string_desc_t a, string_desc_t b);
+#if 0 /* Defined inline below.  */
+extern bool sd_equals ([rw_]string_desc_t a, [rw_]string_desc_t b);
+#endif
 
 /* Return true if S starts with PREFIX.  */
-extern bool sd_startswith (string_desc_t s, string_desc_t prefix);
+#if 0 /* Defined inline below.  */
+extern bool sd_startswith ([rw_]string_desc_t s, [rw_]string_desc_t prefix);
+#endif
 
 /* Return true if S ends with SUFFIX.  */
-extern bool sd_endswith (string_desc_t s, string_desc_t suffix);
+#if 0 /* Defined inline below.  */
+extern bool sd_endswith ([rw_]string_desc_t s, [rw_]string_desc_t suffix);
+#endif
 
 /* Return > 0, == 0, or < 0 if A > B, A == B, A < B.
    This uses a lexicographic ordering, where the bytes are compared as
    'unsigned char'.  */
-extern int sd_cmp (string_desc_t a, string_desc_t b);
+#if 0 /* Defined inline below.  */
+extern int sd_cmp ([rw_]string_desc_t a, [rw_]string_desc_t b);
+#endif
 
 /* Return > 0, == 0, or < 0 if A > B, A == B, A < B.
    Either A or B must be entirely ASCII.
    This uses a lexicographic ordering, where the bytes are compared as
    'unsigned char', ignoring case, in the "C" locale.  */
-extern int sd_c_casecmp (string_desc_t a, string_desc_t b);
+#if 0 /* Defined inline below.  */
+extern int sd_c_casecmp ([rw_]string_desc_t a, [rw_]string_desc_t b);
+#endif
 
 /* Return the index of the first occurrence of C in S,
    or -1 if there is none.  */
-extern ptrdiff_t sd_index (string_desc_t s, char c);
+#if 0 /* Defined inline below.  */
+extern ptrdiff_t sd_index ([rw_]string_desc_t s, char c);
+#endif
 
 /* Return the index of the last occurrence of C in S,
    or -1 if there is none.  */
-extern ptrdiff_t sd_last_index (string_desc_t s, char c);
+#if 0 /* Defined inline below.  */
+extern ptrdiff_t sd_last_index ([rw_]string_desc_t s, char c);
+#endif
 
 /* Return the index of the first occurrence of NEEDLE in HAYSTACK,
    or -1 if there is none.  */
-extern ptrdiff_t sd_contains (string_desc_t haystack, string_desc_t needle);
+#if 0 /* Defined inline below.  */
+extern ptrdiff_t sd_contains ([rw_]string_desc_t haystack, [rw_]string_desc_t needle);
+#endif
 
 /* Return an empty string.  */
 extern string_desc_t sd_new_empty (void);
 
 /* Construct and return a string of length N, at the given memory address.  */
-extern string_desc_t sd_new_addr (idx_t n, char *addr)
+#if 0 /* Defined inline below.  */
+extern string_desc_t sd_new_addr (idx_t n, const char *addr)
   _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
+extern rw_string_desc_t sd_new_addr (idx_t n, char *addr)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
+#endif
 
 /* Return a string that represents the C string S, of length strlen (S).  */
 extern string_desc_t sd_from_c (const char *s);
@@ -135,17 +193,24 @@ extern string_desc_t sd_from_c (const char *s);
    The result is of length END - START.
    The result must not be freed (since its storage is part of the storage
    of S).  */
+#if 0 /* Defined inline below.  */
 extern string_desc_t sd_substring (string_desc_t s, idx_t start, idx_t end);
+extern rw_string_desc_t sd_substring (rw_string_desc_t s, idx_t start, idx_t end);
+#endif
 
 /* Output S to the file descriptor FD.
    Return 0 if successful.
    Upon error, return -1 with errno set.  */
-extern int sd_write (int fd, string_desc_t s);
+#if 0 /* Defined inline below.  */
+extern int sd_write (int fd, [rw_]string_desc_t s);
+#endif
 
 /* Output S to the FILE stream FP.
    Return 0 if successful.
    Upon error, return -1.  */
-extern int sd_fwrite (FILE *fp, string_desc_t s);
+#if 0 /* Defined inline below.  */
+extern int sd_fwrite (FILE *fp, [rw_]string_desc_t s);
+#endif
 
 
 /* ==== Memory-allocating operations on string descriptors ==== */
@@ -154,59 +219,113 @@ extern int sd_fwrite (FILE *fp, string_desc_t s);
    Return 0 if successful.
    Upon error, return -1 with errno set.  */
 _GL_ATTRIBUTE_NODISCARD
-extern int sd_new (string_desc_t *resultp, idx_t n);
+extern int sd_new (rw_string_desc_t *resultp, idx_t n);
 
 /* Construct a string of length N, filled with C.
    Return 0 if successful.
    Upon error, return -1 with errno set.  */
 _GL_ATTRIBUTE_NODISCARD
-extern int sd_new_filled (string_desc_t *resultp, idx_t n, char c);
+extern int sd_new_filled (rw_string_desc_t *resultp, idx_t n, char c);
 
 /* Construct a copy of string S.
    Return 0 if successful.
    Upon error, return -1 with errno set.  */
+#if 0 /* Defined inline below.  */
 _GL_ATTRIBUTE_NODISCARD
-extern int sd_copy (string_desc_t *resultp, string_desc_t s);
+extern int sd_copy (rw_string_desc_t *resultp, [rw_]string_desc_t s);
+#endif
 
 /* Construct the concatenation of N strings.  N must be > 0.
    Return 0 if successful.
    Upon error, return -1 with errno set.  */
 _GL_ATTRIBUTE_NODISCARD
-extern int sd_concat (string_desc_t *resultp, idx_t n, string_desc_t string1, ...);
+extern int sd_concat (rw_string_desc_t *resultp, idx_t n, /* [rw_]string_desc_t string1, */ ...);
 
 /* Construct a copy of string S, as a NUL-terminated C string.
    Return it is successful.
    Upon error, return NULL with errno set.  */
-extern char * sd_c (string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
+#if 0 /* Defined inline below.  */
+extern char * sd_c ([rw_]string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
+#endif
 
 
 /* ==== Operations with side effects on string descriptors ==== */
 
 /* Overwrite the byte at index I of string S with C.
    I must be < length(S).  */
-extern void sd_set_char_at (string_desc_t s, idx_t i, char c);
+extern void sd_set_char_at (rw_string_desc_t s, idx_t i, char c);
 
 /* Fill part of S, starting at offset START and ending at offset END,
    with copies of C.
    START must be <= END.  */
-extern void sd_fill (string_desc_t s, idx_t start, idx_t end, char c);
+extern void sd_fill (rw_string_desc_t s, idx_t start, idx_t end, char c);
 
 /* Overwrite part of S with T, starting at offset START.
    START + length(T) must be <= length (S).  */
-extern void sd_overwrite (string_desc_t s, idx_t start, string_desc_t t);
+#if 0 /* Defined inline below.  */
+extern void sd_overwrite (rw_string_desc_t s, idx_t start, [rw_]string_desc_t t);
+#endif
 
 /* Free S.  */
-extern void sd_free (string_desc_t s);
+extern void sd_free (rw_string_desc_t s);
 
 
 /* ==== Inline function definitions ==== */
 
+#if HAVE_STATEMENT_EXPRESSIONS
+GL_STRING_DESC_INLINE string_desc_t
+sd_readonly (rw_string_desc_t s)
+{
+  string_desc_t result;
+  result._nbytes = s._nbytes;
+  result._data = s._data;
+  return result;
+}
+#else
+# define sd_readonly(s) (s)
+#endif
+
+#if HAVE_STATEMENT_EXPRESSIONS
+GL_STRING_DESC_INLINE rw_string_desc_t
+sd_readwrite (string_desc_t s)
+{
+  rw_string_desc_t result;
+  result._nbytes = s._nbytes;
+  result._data = (char *) s._data;
+  return result;
+}
+#else
+# define sd_readwrite(s) (s)
+#endif
+
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_length(s) \
+   _Generic (s, \
+             string_desc_t    : (s)._nbytes, \
+             rw_string_desc_t : (s)._nbytes)
+#else
 GL_STRING_DESC_INLINE idx_t
 sd_length (string_desc_t s)
 {
   return s._nbytes;
 }
+#endif
 
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_char_at(s, i) \
+   ({typeof (s) _s_ = (s); \
+     _sd_char_at (_s_._nbytes, _s_._data, i); \
+   })
+GL_STRING_DESC_INLINE char
+_sd_char_at (idx_t s_nbytes, const char *s_data, idx_t i)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
+{
+  if (!(i >= 0 && i < s_nbytes))
+    /* Invalid argument.  */
+    abort ();
+  return s_data[i];
+}
+#else
 GL_STRING_DESC_INLINE char
 sd_char_at (string_desc_t s, idx_t i)
 {
@@ -215,18 +334,296 @@ sd_char_at (string_desc_t s, idx_t i)
     abort ();
   return s._data[i];
 }
+#endif
 
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_data(s) \
+   _Generic (s, \
+             string_desc_t    : (s)._data, \
+             rw_string_desc_t : (s)._data)
+#else
 GL_STRING_DESC_INLINE const char *
 sd_data (string_desc_t s)
 {
   return s._data;
 }
+#endif
 
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_is_empty(s) \
+   _Generic (s, \
+             string_desc_t    : (s)._nbytes == 0, \
+             rw_string_desc_t : (s)._nbytes == 0)
+#else
 GL_STRING_DESC_INLINE bool
 sd_is_empty (string_desc_t s)
 {
   return s._nbytes == 0;
 }
+#endif
+
+extern bool _sd_equals (idx_t a_nbytes, const char *a_data,
+                        idx_t b_nbytes, const char *b_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_equals(a, b) \
+   ({typeof (a) _a_ = (a); \
+     typeof (b) _b_ = (b); \
+     _sd_equals (_a_._nbytes, _a_._data, _b_._nbytes, _b_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE bool
+sd_equals (string_desc_t a, string_desc_t b)
+{
+  return _sd_equals (a._nbytes, a._data, b._nbytes, b._data);
+}
+#endif
+
+extern bool _sd_startswith (idx_t s_nbytes, const char *s_data,
+                            idx_t prefix_nbytes, const char *prefix_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_startswith(s, prefix) \
+   ({typeof (s) _s_ = (s); \
+     typeof (prefix) _prefix_ = (prefix); \
+     _sd_startswith (_s_._nbytes, _s_._data, _prefix_._nbytes, _prefix_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE bool
+sd_startswith (string_desc_t s, string_desc_t prefix)
+{
+  return _sd_startswith (s._nbytes, s._data, prefix._nbytes, prefix._data);
+}
+#endif
+
+extern bool _sd_endswith (idx_t s_nbytes, const char *s_data,
+                          idx_t suffix_nbytes, const char *suffix_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_endswith(s, suffix) \
+   ({typeof (s) _s_ = (s); \
+     typeof (suffix) _suffix_ = (suffix); \
+     _sd_endswith (_s_._nbytes, _s_._data, _suffix_._nbytes, _suffix_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE bool
+sd_endswith (string_desc_t s, string_desc_t suffix)
+{
+  return _sd_endswith (s._nbytes, s._data, suffix._nbytes, suffix._data);
+}
+#endif
+
+extern int _sd_cmp (idx_t a_nbytes, const char *a_data,
+                    idx_t b_nbytes, const char *b_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_cmp(a, b) \
+   ({typeof (a) _a_ = (a); \
+     typeof (b) _b_ = (b); \
+     _sd_cmp (_a_._nbytes, _a_._data, _b_._nbytes, _b_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE int
+sd_cmp (string_desc_t a, string_desc_t b)
+{
+  return _sd_cmp (a._nbytes, a._data, b._nbytes, b._data);
+}
+#endif
+
+extern int _sd_c_casecmp (idx_t a_nbytes, const char *a_data,
+                          idx_t b_nbytes, const char *b_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_c_casecmp(a, b) \
+   ({typeof (a) _a_ = (a); \
+     typeof (b) _b_ = (b); \
+     _sd_c_casecmp (_a_._nbytes, _a_._data, _b_._nbytes, _b_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE int
+sd_c_casecmp (string_desc_t a, string_desc_t b)
+{
+  return _sd_c_casecmp (a._nbytes, a._data, b._nbytes, b._data);
+}
+#endif
+
+extern ptrdiff_t _sd_index (idx_t s_nbytes, const char *s_data, char c)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_index(s, c) \
+   ({typeof (s) _s_ = (s); \
+     _sd_index (_s_._nbytes, _s_._data, c); \
+   })
+#else
+GL_STRING_DESC_INLINE ptrdiff_t
+sd_index (string_desc_t s, char c)
+{
+  return _sd_index (s._nbytes, s._data, c);
+}
+#endif
+
+extern ptrdiff_t _sd_last_index (idx_t s_nbytes, const char *s_data, char c)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_last_index(s, c) \
+   ({typeof (s) _s_ = (s); \
+     _sd_last_index (_s_._nbytes, _s_._data, c); \
+   })
+#else
+GL_STRING_DESC_INLINE ptrdiff_t
+sd_last_index (string_desc_t s, char c)
+{
+  return _sd_last_index (s._nbytes, s._data, c);
+}
+#endif
+
+extern ptrdiff_t _sd_contains (idx_t haystack_nbytes, const char *haystack_data,
+                               idx_t needle_nbytes, const char *needle_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_contains(haystack, needle) \
+   ({typeof (haystack) _haystack_ = (haystack); \
+     typeof (needle) _needle_ = (needle); \
+     _sd_contains (_haystack_._nbytes, _haystack_._data, \
+                   _needle_._nbytes, _needle_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE ptrdiff_t
+sd_contains (string_desc_t haystack, string_desc_t needle)
+{
+  return _sd_contains (haystack._nbytes, haystack._data,
+                       needle._nbytes, needle._data);
+}
+#endif
+
+extern string_desc_t _sd_new_addr (idx_t n, const char *addr)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
+extern rw_string_desc_t _rwsd_new_addr (idx_t n, /*!*/const/*!*/ char *addr)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_new_addr(n, addr) \
+   _Generic (addr, \
+             const char * : _sd_new_addr (n, addr), \
+             char *       : _rwsd_new_addr (n, addr))
+#else
+# define sd_new_addr(n, addr) \
+   _rwsd_new_addr (n, addr)
+#endif
+
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_substring(s, start, end) \
+   ({typeof (s) _s_ = (s); \
+     idx_t _start_ = (start); \
+     idx_t _end_ = (end); \
+     if (!(_start_ >= 0 && _start_ <= _end_ && _end_ <= _s_._nbytes)) \
+       /* Invalid arguments.  */ \
+       abort (); \
+     typeof (s) _result_; \
+     _result_._nbytes = _end_ - _start_; \
+     _result_._data = _s_._data + _start_; \
+     _result_; \
+   })
+#else
+GL_STRING_DESC_INLINE string_desc_t
+sd_substring (string_desc_t s, idx_t start, idx_t end)
+{
+  if (!(start >= 0 && start <= end && end <= s._nbytes))
+    /* Invalid arguments.  */
+    abort ();
+  string_desc_t result;
+  result._nbytes = end - start;
+  result._data = s._data + start;
+  return result;
+}
+#endif
+
+extern int _sd_write (int fd, idx_t s_nbytes, const char *s_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_write(fd, s) \
+   ({int _fd_ = (fd); \
+     typeof (s) _s_ = (s); \
+     _sd_write (_fd_, _s_._nbytes, _s_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE int
+sd_write (int fd, string_desc_t s)
+{
+  return _sd_write (fd, s._nbytes, s._data);
+}
+#endif
+
+extern int _sd_fwrite (FILE *fp, idx_t s_nbytes, const char *s_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_fwrite(fp, s) \
+   ({FILE *_fp_ = (fp); \
+     typeof (s) _s_ = (s); \
+     _sd_fwrite (_fp_, _s_._nbytes, _s_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE int
+sd_fwrite (FILE *fp, string_desc_t s)
+{
+  return _sd_fwrite (fp, s._nbytes, s._data);
+}
+#endif
+
+extern int _sd_copy (rw_string_desc_t *resultp,
+                     idx_t s_nbytes, const char *s_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (3, 2);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_copy(resultp, s) \
+   ({rw_string_desc_t *_resultp_ = (resultp); \
+     typeof (s) _s_ = (s); \
+     _sd_copy (_resultp_, _s_._nbytes, _s_._data); \
+   })
+#else
+_GL_ATTRIBUTE_NODISCARD GL_STRING_DESC_INLINE int
+sd_copy (rw_string_desc_t *resultp, string_desc_t s)
+{
+  return _sd_copy (resultp, s._nbytes, s._data);
+}
+#endif
+
+extern char *_sd_c (idx_t s_nbytes, const char *s_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_c(s) \
+   ({typeof (s) _s_ = (s); \
+     _sd_c (_s_._nbytes, _s_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE char *
+sd_c (string_desc_t s)
+{
+  return _sd_c (s._nbytes, s._data);
+}
+#endif
+
+extern void _sd_overwrite (rw_string_desc_t s, idx_t start,
+                           idx_t t_nbytes, const char *t_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (4, 3);
+#if HAVE_STATEMENT_EXPRESSIONS
+# define sd_overwrite(s, start, t) \
+   ({rw_string_desc_t _s_ = (s); \
+     idx_t _start_ = (start); \
+     typeof (t) _t_ = (t); \
+     _sd_overwrite (_s_, _start_, _t_._nbytes, _t_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE void
+sd_overwrite (rw_string_desc_t s, idx_t start, string_desc_t t)
+{
+  return _sd_overwrite (s, start, t._nbytes, t._data);
+}
+#endif
 
 
 #ifdef __cplusplus
index 333ccbd8a2d7b9d27489b5be4fd6ded44cb0c0a2..51d7ffc4f79a3094b2160c396d941dff2a855022 100644 (file)
@@ -44,15 +44,15 @@ sbr_xprepend_c (struct string_buffer_reversed *buffer, const char *str)
     xalloc_die ();
 }
 
-string_desc_t
+rw_string_desc_t
 sbr_xdupfree (struct string_buffer_reversed *buffer)
 {
   if (buffer->error)
     {
       sbr_free (buffer);
-      return sd_new_addr (0, NULL);
+      return sd_new_addr (0, (char *) NULL);
     }
-  string_desc_t contents = sbr_dupfree (buffer);
+  rw_string_desc_t contents = sbr_dupfree (buffer);
   if (sd_data (contents) == NULL)
     xalloc_die ();
   return contents;
index 4ef1a80a806f8fc6f47aae73706ce38d3acf5230..3b2691d12ed89638debe8f37973cb904ffd4a824 100644 (file)
@@ -53,15 +53,15 @@ sb_xcontents_c (struct string_buffer *buffer)
   return contents;
 }
 
-string_desc_t
+rw_string_desc_t
 sb_xdupfree (struct string_buffer *buffer)
 {
   if (buffer->error)
     {
       sb_free (buffer);
-      return sd_new_addr (0, NULL);
+      return sd_new_addr (0, (char *) NULL);
     }
-  string_desc_t contents = sb_dupfree (buffer);
+  rw_string_desc_t contents = sb_dupfree (buffer);
   if (sd_data (contents) == NULL)
     xalloc_die ();
   return contents;
index 69520e2b0ab7e844f3743e9b5009f7e03747f4c6..c44d8bc3b49b25c6c6ed5b927894f8f24a2893ad 100644 (file)
 
 #include "ialloc.h"
 
-string_desc_t
-xsd_concat (idx_t n, string_desc_t string1, ...)
+rw_string_desc_t
+xsd_concat (idx_t n, /* [rw_]string_desc_t string1, */ ...)
 {
   if (n <= 0)
     /* Invalid argument.  */
     abort ();
 
   idx_t total = 0;
-  total += string1._nbytes;
-  if (n > 1)
-    {
-      va_list other_strings;
-      idx_t i;
+  {
+    va_list strings;
+    va_start (strings, n);
+    string_desc_t string1 = va_arg (strings, string_desc_t);
+    total += string1._nbytes;
+    if (n > 1)
+      {
+        idx_t i;
 
-      va_start (other_strings, string1);
-      for (i = n - 1; i > 0; i--)
-        {
-          string_desc_t arg = va_arg (other_strings, string_desc_t);
-          total += arg._nbytes;
-        }
-      va_end (other_strings);
-    }
+        for (i = n - 1; i > 0; i--)
+          {
+            string_desc_t arg = va_arg (strings, string_desc_t);
+            total += arg._nbytes;
+          }
+      }
+    va_end (strings);
+  }
 
   char *combined = (char *) imalloc (total);
   if (combined == NULL)
     xalloc_die ();
   idx_t pos = 0;
-  memcpy (combined, string1._data, string1._nbytes);
-  pos += string1._nbytes;
-  if (n > 1)
-    {
-      va_list other_strings;
-      idx_t i;
+  {
+    va_list strings;
+    va_start (strings, n);
+    string_desc_t string1 = va_arg (strings, string_desc_t);
+    memcpy (combined, string1._data, string1._nbytes);
+    pos += string1._nbytes;
+    if (n > 1)
+      {
+        idx_t i;
 
-      va_start (other_strings, string1);
-      for (i = n - 1; i > 0; i--)
-        {
-          string_desc_t arg = va_arg (other_strings, string_desc_t);
-          if (arg._nbytes > 0)
-            memcpy (combined + pos, arg._data, arg._nbytes);
-          pos += arg._nbytes;
-        }
-      va_end (other_strings);
-    }
+        for (i = n - 1; i > 0; i--)
+          {
+            string_desc_t arg = va_arg (strings, string_desc_t);
+            if (arg._nbytes > 0)
+              memcpy (combined + pos, arg._data, arg._nbytes);
+            pos += arg._nbytes;
+          }
+      }
+    va_end (strings);
+  }
 
-  string_desc_t result;
+  rw_string_desc_t result;
   result._nbytes = total;
   result._data = combined;
 
index 20b3e72a184958c3998b90e910b81c325873a01f..216865a267d09769e785db8d74f32ffdbcc41362 100644 (file)
@@ -20,7 +20,8 @@
 #define _XSTRING_DESC_H 1
 
 /* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE,
-   _GL_ATTRIBUTE_DEALLOC_FREE, _GL_ATTRIBUTE_RETURNS_NONNULL.  */
+   _GL_ATTRIBUTE_DEALLOC_FREE, _GL_ATTRIBUTE_NONNULL_IF_NONZERO,
+   _GL_ATTRIBUTE_RETURNS_NONNULL.  */
 #if !_GL_CONFIG_H_INCLUDED
  #error "Please include config.h first."
 #endif
@@ -44,52 +45,52 @@ extern "C" {
 
 /* Return a string of length N, with uninitialized contents.  */
 #if 0 /* Defined inline below.  */
-extern string_desc_t xsd_new (idx_t n);
+extern rw_string_desc_t xsd_new (idx_t n);
 #endif
 
 /* Return a string of length N, filled with C.  */
 #if 0 /* Defined inline below.  */
-extern string_desc_t xsd_new_filled (idx_t n, char c);
+extern rw_string_desc_t xsd_new_filled (idx_t n, char c);
 #endif
 
 /* Return a copy of string S.  */
 #if 0 /* Defined inline below.  */
-extern string_desc_t xsd_copy (string_desc_t s);
+extern rw_string_desc_t xsd_copy (string_desc_t s);
 #endif
 
 /* Return the concatenation of N strings.  N must be > 0.  */
-extern string_desc_t xsd_concat (idx_t n, string_desc_t string1, ...);
+extern rw_string_desc_t xsd_concat (idx_t n, /* [rw_]string_desc_t string1, */ ...);
 
 /* Construct and return a copy of string S, as a NUL-terminated C string.  */
 #if 0 /* Defined inline below.  */
-extern char * xsd_c (string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
+extern char * xsd_c ([rw_]string_desc_t s) _GL_ATTRIBUTE_DEALLOC_FREE;
 #endif
 
 
 /* ==== Inline function definitions ==== */
 
-GL_XSTRING_DESC_INLINE string_desc_t
+GL_XSTRING_DESC_INLINE rw_string_desc_t
 xsd_new (idx_t n)
 {
-  string_desc_t result;
+  rw_string_desc_t result;
   if (sd_new (&result, n) < 0)
     xalloc_die ();
   return result;
 }
 
-GL_XSTRING_DESC_INLINE string_desc_t
+GL_XSTRING_DESC_INLINE rw_string_desc_t
 xsd_new_filled (idx_t n, char c)
 {
-  string_desc_t result;
+  rw_string_desc_t result;
   if (sd_new_filled (&result, n, c) < 0)
     xalloc_die ();
   return result;
 }
 
-GL_XSTRING_DESC_INLINE string_desc_t
+GL_XSTRING_DESC_INLINE rw_string_desc_t
 xsd_copy (string_desc_t s)
 {
-  string_desc_t result;
+  rw_string_desc_t result;
   if (sd_copy (&result, s) < 0)
     xalloc_die ();
   return result;
@@ -98,13 +99,28 @@ xsd_copy (string_desc_t s)
 GL_XSTRING_DESC_INLINE
 _GL_ATTRIBUTE_DEALLOC_FREE _GL_ATTRIBUTE_RETURNS_NONNULL
 char *
-xsd_c (string_desc_t s)
+_xsd_c (idx_t s_nbytes, const char *s_data)
+  _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 1)
 {
-  char *result = sd_c (s);
+  char *result = _sd_c (s_nbytes, s_data);
   if (result == NULL)
     xalloc_die ();
   return result;
 }
+#if HAVE_STATEMENT_EXPRESSIONS
+# define xsd_c(s) \
+   ({typeof (s) _xs_ = (s); \
+     _xsd_c (_xs_._nbytes, _xs_._data); \
+   })
+#else
+GL_STRING_DESC_INLINE
+_GL_ATTRIBUTE_DEALLOC_FREE _GL_ATTRIBUTE_RETURNS_NONNULL
+char *
+xsd_c (string_desc_t s)
+{
+  return _xsd_c (s._nbytes, s._data);
+}
+#endif
 
 
 #ifdef __cplusplus
index 74c6203e484726073b9a804399faab1d0ae6cae2..0ab488fc73ecd4cf7dcbb9d85d2e5adc713c76c0 100644 (file)
@@ -91,8 +91,7 @@ main ()
   {
     sf_istream_t stream;
     sf_istream_init_from_string_desc (&stream,
-                                      sd_new_addr (CONTENTS_LEN,
-                                                   (char *) contents));
+                                      sd_new_addr (CONTENTS_LEN, contents));
     test_open_stream (&stream);
     sf_free (&stream);
   }
index bd72952c550b38cc63bfe1c1efb018bc50ec86b0..43fdc14aaef9c9763e98403c9aa2b3aac0491e84 100644 (file)
@@ -115,8 +115,7 @@ main ()
   {
     sfl_istream_t stream;
     sfl_istream_init_from_string_desc (&stream,
-                                       sd_new_addr (CONTENTS_LEN,
-                                                    (char *) contents));
+                                       sd_new_addr (CONTENTS_LEN, contents));
     test_open_stream (&stream);
     sfl_free (&stream);
   }
index d90b548f6af8281a3ecefae6e2ac6b74b146c513..0b2a5406a57b10828c66b023cfe02f3dd7425ac3 100644 (file)
@@ -142,7 +142,7 @@ main (int argc, char *argv[])
   ASSERT (sd_fwrite (stdout, s2) == 0);
 
   /* Test sd_new, sd_set_char_at, sd_fill.  */
-  string_desc_t s4;
+  rw_string_desc_t s4;
   ASSERT (sd_new (&s4, 5) == 0);
   sd_set_char_at (s4, 0, 'H');
   sd_set_char_at (s4, 4, 'o');
@@ -152,7 +152,7 @@ main (int argc, char *argv[])
   ASSERT (sd_startswith (s1, s4));
 
   /* Test sd_new_filled, sd_set_char_at.  */
-  string_desc_t s5;
+  rw_string_desc_t s5;
   ASSERT (sd_new_filled (&s5, 5, 'l') == 0);
   sd_set_char_at (s5, 0, 'H');
   sd_set_char_at (s5, 4, 'o');
@@ -166,13 +166,13 @@ main (int argc, char *argv[])
 
   /* Test sd_copy, sd_free.  */
   {
-    string_desc_t s6;
+    rw_string_desc_t s6;
     ASSERT (sd_copy (&s6, s0) == 0);
     ASSERT (sd_is_empty (s6));
     sd_free (s6);
   }
   {
-    string_desc_t s6;
+    rw_string_desc_t s6;
     ASSERT (sd_copy (&s6, s2) == 0);
     ASSERT (sd_equals (s6, s2));
     sd_free (s6);
@@ -180,7 +180,7 @@ main (int argc, char *argv[])
 
   /* Test sd_overwrite.  */
   {
-    string_desc_t s7;
+    rw_string_desc_t s7;
     ASSERT (sd_copy (&s7, s2) == 0);
     sd_overwrite (s7, 4, s1);
     ASSERT (sd_equals (s7, sd_new_addr (21, "The\0Hello world!\0fox")));
@@ -188,7 +188,7 @@ main (int argc, char *argv[])
 
   /* Test sd_concat.  */
   {
-    string_desc_t s8;
+    rw_string_desc_t s8;
     ASSERT (sd_concat (&s8, 3, sd_new_addr (10, "The\0quick"),
                                sd_new_addr (7, "brown\0"),
                                sd_new_addr (4, "fox"),
index d74063404992777cd481e9e48671c16780437169..02f22c2be5cd9d7e8c52114f1ba932bc83dfb291 100644 (file)
@@ -33,7 +33,7 @@ main (void)
   string_desc_t s2 = sd_new_addr (21, "The\0quick\0brown\0\0fox");
 
   /* Test xsd_new.  */
-  string_desc_t s4 = xsd_new (5);
+  rw_string_desc_t s4 = xsd_new (5);
   sd_set_char_at (s4, 0, 'H');
   sd_set_char_at (s4, 4, 'o');
   sd_set_char_at (s4, 1, 'e');
@@ -42,7 +42,7 @@ main (void)
   ASSERT (sd_startswith (s1, s4));
 
   /* Test xsd_new_filled.  */
-  string_desc_t s5 = xsd_new_filled (5, 'l');
+  rw_string_desc_t s5 = xsd_new_filled (5, 'l');
   sd_set_char_at (s5, 0, 'H');
   sd_set_char_at (s5, 4, 'o');
   sd_set_char_at (s5, 1, 'e');
@@ -51,19 +51,19 @@ main (void)
 
   /* Test xsd_copy.  */
   {
-    string_desc_t s6 = xsd_copy (s0);
+    rw_string_desc_t s6 = xsd_copy (s0);
     ASSERT (sd_is_empty (s6));
     sd_free (s6);
   }
   {
-    string_desc_t s6 = xsd_copy (s2);
+    rw_string_desc_t s6 = xsd_copy (s2);
     ASSERT (sd_equals (s6, s2));
     sd_free (s6);
   }
 
   /* Test xsd_concat.  */
   {
-    string_desc_t s8 =
+    rw_string_desc_t s8 =
       xsd_concat (3, sd_new_addr (10, "The\0quick"),
                      sd_new_addr (7, "brown\0"),
                      sd_new_addr (4, "fox"),